Fixed visible books count not updating
This commit is contained in:
parent
e9a331292a
commit
720fd64c97
@ -0,0 +1,53 @@
|
|||||||
|
<TemplatedControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:controls="clr-namespace:LibationWinForms.AvaloniaUI.Controls"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="LibationWinForms.AvaloniaUI.Controls.DirectorySelectControl">
|
||||||
|
|
||||||
|
<Design.DataContext>
|
||||||
|
</Design.DataContext>
|
||||||
|
|
||||||
|
|
||||||
|
<TemplatedControl.Styles>
|
||||||
|
<Style Selector="controls|DirectorySelectControl Border">
|
||||||
|
<Setter Property="BorderBrush" Value="DarkGray" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="controls|DirectorySelectControl">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
|
||||||
|
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<StackPanel.Styles>
|
||||||
|
<Style Selector="ItemsPresenter#PART_ItemsPresenter">
|
||||||
|
<Setter Property="Height" Value="NaN"/>
|
||||||
|
</Style>
|
||||||
|
</StackPanel.Styles>
|
||||||
|
<ComboBox
|
||||||
|
HorizontalContentAlignment = "Stretch"
|
||||||
|
HorizontalAlignment = "Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
VerticalContentAlignment="Stretch"
|
||||||
|
SelectedItem="{Binding SelectedComboBoxItem, Mode=TwoWay}"
|
||||||
|
Items="{Binding ComboBoxItems}">
|
||||||
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
FontSize="12"
|
||||||
|
Text="{Binding Description}" />
|
||||||
|
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
<TextBox Margin="0,10,0,10" Text="{Binding SelectedComboBoxItem.UiDisplayPath}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</TemplatedControl.Styles>
|
||||||
|
|
||||||
|
</TemplatedControl>
|
||||||
@ -0,0 +1,146 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Dinah.Core;
|
||||||
|
using LibationFileManager;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using ReactiveUI;
|
||||||
|
using Avalonia.Controls.Primitives;
|
||||||
|
using System.Collections;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using Avalonia.Data;
|
||||||
|
|
||||||
|
namespace LibationWinForms.AvaloniaUI.Controls
|
||||||
|
{
|
||||||
|
public class TextCaseConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is Configuration.KnownDirectories dir)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
return new BindingNotification(new InvalidCastException(), BindingErrorType.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public partial class DirectorySelectControl : TemplatedControl
|
||||||
|
{
|
||||||
|
private static readonly List<Configuration.KnownDirectories> defaultList = new List<Configuration.KnownDirectories>()
|
||||||
|
{
|
||||||
|
Configuration.KnownDirectories.WinTemp,
|
||||||
|
Configuration.KnownDirectories.UserProfile,
|
||||||
|
Configuration.KnownDirectories.AppDir,
|
||||||
|
Configuration.KnownDirectories.MyDocs,
|
||||||
|
Configuration.KnownDirectories.LibationFiles
|
||||||
|
};
|
||||||
|
public static readonly StyledProperty<Configuration.KnownDirectories?> SelectedirectoryProperty =
|
||||||
|
AvaloniaProperty.Register<DirectorySelectControl, Configuration.KnownDirectories?>(nameof(Selectedirectory), defaultList[0]);
|
||||||
|
|
||||||
|
public static readonly StyledProperty<List<Configuration.KnownDirectories>> KnownDirectoriesProperty =
|
||||||
|
AvaloniaProperty.Register<DirectorySelectControl, List<Configuration.KnownDirectories>>(nameof(KnownDirectories), defaultList);
|
||||||
|
|
||||||
|
public static readonly StyledProperty<string?> SubdirectoryProperty =
|
||||||
|
AvaloniaProperty.Register<DirectorySelectControl, string?>(nameof(Subdirectory), "subdir");
|
||||||
|
|
||||||
|
DirectorySelectViewModel DirectorySelect { get; } = new();
|
||||||
|
public DirectorySelectControl()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
DirectorySelect.Directories.Clear();
|
||||||
|
|
||||||
|
int insertIndex = 0;
|
||||||
|
foreach (var kd in KnownDirectories.Distinct())
|
||||||
|
DirectorySelect.Directories.Insert(insertIndex++, new(this, kd));
|
||||||
|
|
||||||
|
DataContext = DirectorySelect;
|
||||||
|
base.OnInitialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Configuration.KnownDirectories> KnownDirectories
|
||||||
|
{
|
||||||
|
get { return GetValue(KnownDirectoriesProperty); }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetValue(KnownDirectoriesProperty, value);
|
||||||
|
//SetDirectoryItems(KnownDirectories);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Configuration.KnownDirectories? Selectedirectory
|
||||||
|
{
|
||||||
|
get { return GetValue(SelectedirectoryProperty); }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetValue(SelectedirectoryProperty, value);
|
||||||
|
|
||||||
|
if (value is null or Configuration.KnownDirectories.None)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// set default
|
||||||
|
var item = DirectorySelect.Directories.SingleOrDefault(item => item.Value == value.Value);
|
||||||
|
if (item is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DirectorySelect.SelectedDirectory = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public string? Subdirectory
|
||||||
|
{
|
||||||
|
get { return GetValue(SubdirectoryProperty); }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetValue(SubdirectoryProperty, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DirectorySelectViewModel : ViewModels.ViewModelBase
|
||||||
|
{
|
||||||
|
public class DirectoryComboBoxItem
|
||||||
|
{
|
||||||
|
private readonly DirectorySelectControl _parentControl;
|
||||||
|
public string Description { get; }
|
||||||
|
public Configuration.KnownDirectories Value { get; }
|
||||||
|
|
||||||
|
public string FullPath => AddSubDirectoryToPath(Configuration.GetKnownDirectoryPath(Value));
|
||||||
|
|
||||||
|
/// <summary>Displaying relative paths is confusing. UI should display absolute equivalent</summary>
|
||||||
|
public string UiDisplayPath => Value == Configuration.KnownDirectories.AppDir ? AddSubDirectoryToPath(Configuration.AppDir_Absolute) : FullPath;
|
||||||
|
|
||||||
|
public DirectoryComboBoxItem(DirectorySelectControl parentControl, Configuration.KnownDirectories knownDirectory)
|
||||||
|
{
|
||||||
|
_parentControl = parentControl;
|
||||||
|
Value = knownDirectory;
|
||||||
|
Description = Value.GetDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal string AddSubDirectoryToPath(string path) => string.IsNullOrWhiteSpace(_parentControl.Subdirectory) ? path : System.IO.Path.Combine(path, _parentControl.Subdirectory);
|
||||||
|
|
||||||
|
public override string ToString() => Description;
|
||||||
|
}
|
||||||
|
public ObservableCollection<DirectoryComboBoxItem> Directories { get; } = new(new());
|
||||||
|
private DirectoryComboBoxItem _selectedDirectory;
|
||||||
|
public DirectoryComboBoxItem SelectedDirectory { get => _selectedDirectory; set => this.RaiseAndSetIfChanged(ref _selectedDirectory, value); }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -86,10 +86,8 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
|
|||||||
GridEntries = new GridEntryCollection(CreateGridEntries(dbBooks));
|
GridEntries = new GridEntryCollection(CreateGridEntries(dbBooks));
|
||||||
GridEntries.CollapseAll();
|
GridEntries.CollapseAll();
|
||||||
|
|
||||||
int bookEntryCount = GridEntries.BookEntries().Count();
|
|
||||||
|
|
||||||
InitialLoaded?.Invoke(this, EventArgs.Empty);
|
InitialLoaded?.Invoke(this, EventArgs.Empty);
|
||||||
VisibleCountChanged?.Invoke(this, bookEntryCount);
|
VisibleCountChanged?.Invoke(this, GridEntries.BookEntries().Count());
|
||||||
|
|
||||||
RegisterCollectionChanged();
|
RegisterCollectionChanged();
|
||||||
}
|
}
|
||||||
@ -112,7 +110,12 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
|
|||||||
|
|
||||||
var existingSeriesEntries = GridEntries.AllItems().SeriesEntries().ToList();
|
var existingSeriesEntries = GridEntries.AllItems().SeriesEntries().ToList();
|
||||||
|
|
||||||
await Dispatcher.UIThread.InvokeAsync(() => GridEntries.ReplaceList(newEntries));
|
await Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
GridEntries.ReplaceList(newEntries);
|
||||||
|
GridEntries.Filter = existingFilter;
|
||||||
|
ReSort();
|
||||||
|
});
|
||||||
|
|
||||||
//We're replacing the list, so preserve usere's existing collapse/expand
|
//We're replacing the list, so preserve usere's existing collapse/expand
|
||||||
//state. When resetting a list, default state is open.
|
//state. When resetting a list, default state is open.
|
||||||
@ -122,11 +125,8 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
|
|||||||
if (sEntry is SeriesEntry se && !series.Liberate.Expanded)
|
if (sEntry is SeriesEntry se && !series.Liberate.Expanded)
|
||||||
await Dispatcher.UIThread.InvokeAsync(() => GridEntries.CollapseItem(se));
|
await Dispatcher.UIThread.InvokeAsync(() => GridEntries.CollapseItem(se));
|
||||||
}
|
}
|
||||||
await Dispatcher.UIThread.InvokeAsync(() =>
|
|
||||||
{
|
await Dispatcher.UIThread.InvokeAsync(() => VisibleCountChanged?.Invoke(this, GridEntries.BookEntries().Count()));
|
||||||
GridEntries.Filter = existingFilter;
|
|
||||||
ReSort();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -36,6 +36,7 @@
|
|||||||
Width="60"
|
Width="60"
|
||||||
Height="30"
|
Height="30"
|
||||||
Content="X"
|
Content="X"
|
||||||
|
IsEnabled="{Binding !IsDefault}"
|
||||||
Click="DeleteButton_Clicked" />
|
Click="DeleteButton_Clicked" />
|
||||||
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
@ -50,6 +51,7 @@
|
|||||||
Width="60"
|
Width="60"
|
||||||
Height="30"
|
Height="30"
|
||||||
Content="Export"
|
Content="Export"
|
||||||
|
IsEnabled="{Binding !IsDefault}"
|
||||||
ToolTip.Tip="Export account authorization to audible-cli"
|
ToolTip.Tip="Export account authorization to audible-cli"
|
||||||
Click="ExportButton_Clicked" />
|
Click="ExportButton_Clicked" />
|
||||||
|
|
||||||
@ -71,13 +73,13 @@
|
|||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<ComboBox
|
<ComboBox
|
||||||
MinHeight="30"
|
MinHeight="30"
|
||||||
HorizontalAlignment="Center"
|
HorizontalContentAlignment = "Stretch"
|
||||||
|
HorizontalAlignment = "Stretch"
|
||||||
SelectedItem="{Binding SelectedLocale, Mode=TwoWay}"
|
SelectedItem="{Binding SelectedLocale, Mode=TwoWay}"
|
||||||
Items="{Binding Locales}">
|
Items="{Binding Locales}">
|
||||||
|
|
||||||
<ComboBox.ItemTemplate>
|
<ComboBox.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
|
|
||||||
<TextBlock ZIndex="2"
|
<TextBlock ZIndex="2"
|
||||||
FontSize="12"
|
FontSize="12"
|
||||||
Text="{Binding Name}" />
|
Text="{Binding Name}" />
|
||||||
@ -101,26 +103,12 @@
|
|||||||
Margin="10"
|
Margin="10"
|
||||||
ColumnDefinitions="*,Auto" >
|
ColumnDefinitions="*,Auto" >
|
||||||
|
|
||||||
<StackPanel
|
|
||||||
Grid.Column="0"
|
|
||||||
Orientation="Horizontal">
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Height="30"
|
Height="30"
|
||||||
Content="Add an Account"
|
|
||||||
Name="AddAccountButton"
|
|
||||||
Click="AddAccountButton_Clicked" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
Grid.Column="0"
|
|
||||||
Height="30"
|
|
||||||
Margin="20,0,0,0"
|
|
||||||
Content="Import from audible-cli"
|
Content="Import from audible-cli"
|
||||||
Click="ImportButton_Clicked" />
|
Click="ImportButton_Clicked" />
|
||||||
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Height="30"
|
Height="30"
|
||||||
|
|||||||
@ -1,34 +1,52 @@
|
|||||||
using AudibleUtilities;
|
using AudibleUtilities;
|
||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using ReactiveUI;
|
||||||
|
using AudibleApi;
|
||||||
|
|
||||||
namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
||||||
{
|
{
|
||||||
public partial class AccountsDialog : DialogWindow
|
public partial class AccountsDialog : DialogWindow
|
||||||
{
|
{
|
||||||
public ObservableCollection<AccountDto> Accounts { get; } = new();
|
public ObservableCollection<AccountDto> Accounts { get; } = new();
|
||||||
public class AccountDto
|
public class AccountDto : ViewModels.ViewModelBase
|
||||||
{
|
{
|
||||||
public IList<AudibleApi.Locale> Locales { get; init; }
|
private string _accountId;
|
||||||
|
private Locale _selectedLocale;
|
||||||
|
public IReadOnlyList<Locale> Locales => AccountsDialog.Locales;
|
||||||
public bool LibraryScan { get; set; } = true;
|
public bool LibraryScan { get; set; } = true;
|
||||||
public string AccountId { get; set; }
|
public string AccountId
|
||||||
public AudibleApi.Locale SelectedLocale { get; set; }
|
{
|
||||||
|
get => _accountId;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
this.RaiseAndSetIfChanged(ref _accountId, value);
|
||||||
|
this.RaisePropertyChanged(nameof(IsDefault));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public Locale SelectedLocale
|
||||||
|
{
|
||||||
|
get => _selectedLocale;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
this.RaiseAndSetIfChanged(ref _selectedLocale, value);
|
||||||
|
this.RaisePropertyChanged(nameof(IsDefault));
|
||||||
|
}
|
||||||
|
}
|
||||||
public string AccountName { get; set; }
|
public string AccountName { get; set; }
|
||||||
public bool IsDefault => AccountId is null && SelectedLocale is null && AccountName is null;
|
public bool IsDefault => string.IsNullOrEmpty(AccountId) && SelectedLocale is null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetAudibleCliAppDataPath()
|
private static string GetAudibleCliAppDataPath()
|
||||||
=> Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Audible");
|
=> Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Audible");
|
||||||
|
|
||||||
private List<AudibleApi.Locale> Locales => AudibleApi.Localization.Locales.OrderBy(l => l.Name).ToList();
|
private static IReadOnlyList<Locale> Locales => Localization.Locales.OrderBy(l => l.Name).ToList();
|
||||||
public AccountsDialog()
|
public AccountsDialog()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@ -41,26 +59,51 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
if (!accounts.Any())
|
if (!accounts.Any())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
ControlToFocusOnShow = this.FindControl<Button>(nameof(AddAccountButton));
|
|
||||||
|
|
||||||
DataContext = this;
|
DataContext = this;
|
||||||
|
|
||||||
foreach (var account in accounts)
|
foreach (var account in accounts)
|
||||||
AddAccountToGrid(account);
|
AddAccountToGrid(account);
|
||||||
|
|
||||||
|
var newBlank = new AccountDto();
|
||||||
|
newBlank.PropertyChanged += AccountDto_PropertyChanged;
|
||||||
|
Accounts.Insert(Accounts.Count, newBlank);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddAccountToGrid(Account account)
|
private void AddAccountToGrid(Account account)
|
||||||
{
|
{
|
||||||
//ObservableCollection doesn't fire CollectionChanged on Add, so use Insert instead
|
AccountDto accountDto = new()
|
||||||
Accounts.Insert(Accounts.Count, new()
|
|
||||||
{
|
{
|
||||||
LibraryScan = account.LibraryScan,
|
LibraryScan = account.LibraryScan,
|
||||||
AccountId = account.AccountId,
|
AccountId = account.AccountId,
|
||||||
SelectedLocale = Locales.Single(l => l.Name == account.Locale.Name),
|
SelectedLocale = Locales.Single(l => l.Name == account.Locale.Name),
|
||||||
AccountName = account.AccountName,
|
AccountName = account.AccountName,
|
||||||
Locales = Locales
|
};
|
||||||
});
|
accountDto.PropertyChanged += AccountDto_PropertyChanged;
|
||||||
|
|
||||||
|
//ObservableCollection doesn't fire CollectionChanged on Add, so use Insert instead
|
||||||
|
Accounts.Insert(Accounts.Count, accountDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AccountDto_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (Accounts.Any(a => a.IsDefault))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var newBlank = new AccountDto();
|
||||||
|
newBlank.PropertyChanged += AccountDto_PropertyChanged;
|
||||||
|
Accounts.Insert(Accounts.Count, newBlank);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Source is Button expBtn && expBtn.DataContext is AccountDto acc)
|
||||||
|
{
|
||||||
|
var index = Accounts.IndexOf(acc);
|
||||||
|
if (index < 0) return;
|
||||||
|
|
||||||
|
acc.PropertyChanged -= AccountDto_PropertyChanged;
|
||||||
|
Accounts.Remove(acc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void ImportButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public async void ImportButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
@ -109,21 +152,6 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddAccountButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (Accounts.Any(a => a.IsDefault))
|
|
||||||
return;
|
|
||||||
|
|
||||||
//ObservableCollection doesn't fire CollectionChanged on Add, so use Insert instead
|
|
||||||
Accounts.Insert(Accounts.Count, new() { Locales = Locales });
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DeleteButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.Source is Button expBtn && expBtn.DataContext is AccountDto acc)
|
|
||||||
Accounts.Remove(acc);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ExportButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public void ExportButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Source is Button expBtn && expBtn.DataContext is AccountDto acc)
|
if (e.Source is Button expBtn && expBtn.DataContext is AccountDto acc)
|
||||||
@ -177,7 +205,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
var existing = existingAccounts[i];
|
var existing = existingAccounts[i];
|
||||||
if (!Accounts.Any(dto =>
|
if (!Accounts.Any(dto =>
|
||||||
dto.AccountId?.ToLower().Trim() == existing.AccountId.ToLower()
|
dto.AccountId?.ToLower().Trim() == existing.AccountId.ToLower()
|
||||||
&& dto.SelectedLocale.Name == existing.Locale?.Name))
|
&& dto.SelectedLocale?.Name == existing.Locale?.Name))
|
||||||
{
|
{
|
||||||
accountsSettings.Delete(existing);
|
accountsSettings.Delete(existing);
|
||||||
}
|
}
|
||||||
@ -186,11 +214,11 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
// upsert each. validation occurs through Account and AccountsSettings
|
// upsert each. validation occurs through Account and AccountsSettings
|
||||||
foreach (var dto in Accounts)
|
foreach (var dto in Accounts)
|
||||||
{
|
{
|
||||||
var acct = accountsSettings.Upsert(dto.AccountId, dto.SelectedLocale.Name);
|
var acct = accountsSettings.Upsert(dto.AccountId, dto.SelectedLocale?.Name);
|
||||||
acct.LibraryScan = dto.LibraryScan;
|
acct.LibraryScan = dto.LibraryScan;
|
||||||
acct.AccountName
|
acct.AccountName
|
||||||
= string.IsNullOrWhiteSpace(dto.AccountName)
|
= string.IsNullOrWhiteSpace(dto.AccountName)
|
||||||
? $"{dto.AccountId} - {dto.SelectedLocale.Name}"
|
? $"{dto.AccountId} - {dto.SelectedLocale?.Name}"
|
||||||
: dto.AccountName.Trim();
|
: dto.AccountName.Trim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,7 +238,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(dto.SelectedLocale.Name))
|
if (string.IsNullOrWhiteSpace(dto.SelectedLocale?.Name))
|
||||||
{
|
{
|
||||||
await MessageBox.Show(this, "Please select a locale (i.e.: country or region) for all accounts.", "Blank region", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
await MessageBox.Show(this, "Please select a locale (i.e.: country or region) for all accounts.", "Blank region", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
return false;
|
return false;
|
||||||
@ -225,7 +253,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
// without transaction, accounts persister will write ANY EDIT immediately to file
|
// without transaction, accounts persister will write ANY EDIT immediately to file
|
||||||
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||||
|
|
||||||
var account = persister.AccountsSettings.Accounts.FirstOrDefault(a => a.AccountId == acc.AccountId && a.Locale.Name == acc.SelectedLocale.Name);
|
var account = persister.AccountsSettings.Accounts.FirstOrDefault(a => a.AccountId == acc.AccountId && a.Locale.Name == acc.SelectedLocale?.Name);
|
||||||
|
|
||||||
if (account is null)
|
if (account is null)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -0,0 +1,406 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="600"
|
||||||
|
x:Class="LibationWinForms.AvaloniaUI.Views.Dialogs.SettingsDialog"
|
||||||
|
xmlns:controls="clr-namespace:LibationWinForms.AvaloniaUI.Controls"
|
||||||
|
Title="Edit Settings"
|
||||||
|
Icon="/AvaloniaUI/Assets/libation.ico">
|
||||||
|
|
||||||
|
<Grid RowDefinitions="*,Auto">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
Grid.Row="1"
|
||||||
|
Margin="10"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Height="30"
|
||||||
|
Padding="30,3,30,3"
|
||||||
|
Content="Save"
|
||||||
|
Click="SaveButton_Clicked" />
|
||||||
|
|
||||||
|
<TabControl Grid.Column="0">
|
||||||
|
<TabControl.Styles>
|
||||||
|
<Style Selector="ItemsPresenter#PART_ItemsPresenter">
|
||||||
|
<Setter Property="Height" Value="33"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="TextBlock">
|
||||||
|
<Setter Property="FontSize" Value="12"/>
|
||||||
|
</Style>
|
||||||
|
</TabControl.Styles>
|
||||||
|
|
||||||
|
<TabItem>
|
||||||
|
|
||||||
|
<TabItem.Header>
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
FontSize="14"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Download/Decrypt"/>
|
||||||
|
|
||||||
|
</TabItem.Header>
|
||||||
|
<Border
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.Row="0"
|
||||||
|
BorderThickness="2"
|
||||||
|
Background="WhiteSmoke"
|
||||||
|
BorderBrush="{DynamicResource DataGridGridLinesBrush}">
|
||||||
|
|
||||||
|
<Grid RowDefinitions="Auto,Auto,*">
|
||||||
|
<controls:GroupBox Grid.Row="0" BorderWidth="1" Label="{Binding DownloadDecryptSettings.BadBookGroupboxText}">
|
||||||
|
|
||||||
|
<Grid ColumnDefinitions="*,*" RowDefinitions="Auto,Auto">
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.Row="0"
|
||||||
|
Margin="0,5,0,5"
|
||||||
|
IsChecked="{Binding DownloadDecryptSettings.BadBookAsk, Mode=TwoWay}">
|
||||||
|
<TextBlock Text="{Binding DownloadDecryptSettings.BadBookAskText}" />
|
||||||
|
</RadioButton>
|
||||||
|
<RadioButton
|
||||||
|
Grid.Column="1"
|
||||||
|
Grid.Row="0"
|
||||||
|
Margin="0,5,0,5"
|
||||||
|
IsChecked="{Binding DownloadDecryptSettings.BadBookAbort, Mode=TwoWay}">
|
||||||
|
<TextBlock Text="{Binding DownloadDecryptSettings.BadBookAbortText}" />
|
||||||
|
</RadioButton>
|
||||||
|
<RadioButton
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.Row="1"
|
||||||
|
Margin="0,5,0,5"
|
||||||
|
IsChecked="{Binding DownloadDecryptSettings.BadBookRetry, Mode=TwoWay}">
|
||||||
|
<TextBlock Text="{Binding DownloadDecryptSettings.BadBookRetryText}" />
|
||||||
|
</RadioButton>
|
||||||
|
<RadioButton
|
||||||
|
Grid.Column="1"
|
||||||
|
Grid.Row="1"
|
||||||
|
Margin="0,5,0,5"
|
||||||
|
IsChecked="{Binding DownloadDecryptSettings.BadBookIgnore, Mode=TwoWay}">
|
||||||
|
<TextBlock Text="{Binding DownloadDecryptSettings.BadBookIgnoreText}" />
|
||||||
|
</RadioButton>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</controls:GroupBox>
|
||||||
|
|
||||||
|
<controls:GroupBox Grid.Row="1" BorderWidth="1" Label="Custom File Naming">
|
||||||
|
|
||||||
|
<Grid RowDefinitions="Auto,Auto,Auto,Auto" ColumnDefinitions="*,Auto">
|
||||||
|
|
||||||
|
<TextBox
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,10,10,10"
|
||||||
|
FontSize="14"
|
||||||
|
IsReadOnly="True"
|
||||||
|
Text="{Binding DownloadDecryptSettings.FolderTemplate}" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
Content="Edit"
|
||||||
|
Height="30"
|
||||||
|
Padding="30,3,30,3"
|
||||||
|
Click="EditFolderTemplateButton_Click" />
|
||||||
|
|
||||||
|
<TextBox
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,10,10,10"
|
||||||
|
FontSize="14"
|
||||||
|
IsReadOnly="True"
|
||||||
|
Text="{Binding DownloadDecryptSettings.FileTemplate}" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
Content="Edit"
|
||||||
|
Height="30"
|
||||||
|
Padding="30,3,30,3"
|
||||||
|
Click="EditFileTemplateButton_Click" />
|
||||||
|
|
||||||
|
<TextBox
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,10,10,10"
|
||||||
|
FontSize="14"
|
||||||
|
IsReadOnly="True"
|
||||||
|
Text="{Binding DownloadDecryptSettings.ChapterFileTemplate}" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.Column="1"
|
||||||
|
Content="Edit"
|
||||||
|
Height="30"
|
||||||
|
Padding="30,3,30,3"
|
||||||
|
Click="EditChapterFileTemplateButton_Click" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
Grid.Row="3"
|
||||||
|
Grid.Column="0"
|
||||||
|
Content="{Binding DownloadDecryptSettings.EditCharReplacementText}"
|
||||||
|
Height="30"
|
||||||
|
Padding="30,3,30,3"
|
||||||
|
Click="EditChapterTitleTemplateButton_Click" />
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</controls:GroupBox>
|
||||||
|
|
||||||
|
<controls:DirectorySelectControl
|
||||||
|
Grid.Row="2"
|
||||||
|
Subdirectory="{Binding DownloadDecryptSettings.EditCharReplacementText}"
|
||||||
|
KnownDirectories="{Binding DownloadDecryptSettings.KnownDirectories}"
|
||||||
|
Selectedirectory="{Binding DownloadDecryptSettings.SelectedDirectory, Mode=TwoWay}" />
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
<TabItem>
|
||||||
|
|
||||||
|
<TabItem.Header>
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
FontSize="14"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Audio File Settings"/>
|
||||||
|
|
||||||
|
</TabItem.Header>
|
||||||
|
<Border
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.Row="0"
|
||||||
|
BorderThickness="2"
|
||||||
|
Background="WhiteSmoke"
|
||||||
|
BorderBrush="{DynamicResource DataGridGridLinesBrush}">
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
RowDefinitions="*,Auto"
|
||||||
|
ColumnDefinitions="*,*">
|
||||||
|
|
||||||
|
<StackPanel Margin="5"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0">
|
||||||
|
|
||||||
|
<CheckBox IsChecked="{Binding AudioSettings.CreateCueSheet, Mode=TwoWay}">
|
||||||
|
<TextBlock Text="{Binding AudioSettings.CreateCueSheetText}" />
|
||||||
|
</CheckBox>
|
||||||
|
<CheckBox IsChecked="{Binding AudioSettings.DownloadCoverArt, Mode=TwoWay}">
|
||||||
|
<TextBlock Text="{Binding AudioSettings.DownloadCoverArtText}" />
|
||||||
|
</CheckBox>
|
||||||
|
<CheckBox IsChecked="{Binding AudioSettings.RetainAaxFile, Mode=TwoWay}">
|
||||||
|
<TextBlock Text="{Binding AudioSettings.RetainAaxFileText}" />
|
||||||
|
</CheckBox>
|
||||||
|
<CheckBox IsChecked="{Binding AudioSettings.MergeOpeningAndEndCredits, Mode=TwoWay}">
|
||||||
|
<TextBlock Text="{Binding AudioSettings.MergeOpeningEndCreditsText}" />
|
||||||
|
</CheckBox>
|
||||||
|
<CheckBox IsChecked="{Binding AudioSettings.AllowLibationFixup, Mode=TwoWay}">
|
||||||
|
<TextBlock Text="{Binding AudioSettings.AllowLibationFixupText}" />
|
||||||
|
</CheckBox>
|
||||||
|
|
||||||
|
<controls:GroupBox BorderWidth="1" Label="Audiobook Fix-ups" IsEnabled="{Binding AudioSettings.AllowLibationFixup}">
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<CheckBox IsChecked="{Binding AudioSettings.SplitFilesByChapter, Mode=TwoWay}">
|
||||||
|
<TextBlock Text="{Binding AudioSettings.SplitFilesByChapterText}" />
|
||||||
|
</CheckBox>
|
||||||
|
|
||||||
|
<CheckBox IsChecked="{Binding AudioSettings.StripAudibleBrandAudio, Mode=TwoWay}">
|
||||||
|
<TextBlock Text="{Binding AudioSettings.StripAudibleBrandingText}" />
|
||||||
|
</CheckBox>
|
||||||
|
|
||||||
|
<CheckBox IsChecked="{Binding AudioSettings.StripUnabridged, Mode=TwoWay}">
|
||||||
|
<TextBlock Text="{Binding AudioSettings.StripUnabridgedText}" />
|
||||||
|
</CheckBox>
|
||||||
|
|
||||||
|
<RadioButton Margin="0,5,0,5" IsChecked="{Binding !AudioSettings.DecryptToLossy, Mode=TwoWay}">
|
||||||
|
<TextBlock Text="Download my books in the original audio format (Lossless)" />
|
||||||
|
</RadioButton>
|
||||||
|
|
||||||
|
<RadioButton Margin="0,5,0,5" IsChecked="{Binding AudioSettings.DecryptToLossy, Mode=TwoWay}">
|
||||||
|
<TextBlock Text="Download my books as .MP3 files (transcode if necessary)" />
|
||||||
|
</RadioButton>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
</controls:GroupBox>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
|
||||||
|
<StackPanel
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="1">
|
||||||
|
|
||||||
|
<controls:GroupBox BorderWidth="1" Label="Mp3 Encoding Options">
|
||||||
|
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<Grid Margin="5,5,5,0" ColumnDefinitions="Auto,*">
|
||||||
|
|
||||||
|
<controls:GroupBox BorderWidth="1" Grid.Column="0" Label="Target">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
|
||||||
|
<RadioButton Margin="10" IsChecked="{Binding AudioSettings.LameTargetBitrate, Mode=TwoWay}">
|
||||||
|
<TextBlock Text="Bitrate" />
|
||||||
|
</RadioButton>
|
||||||
|
|
||||||
|
<RadioButton Margin="10" IsChecked="{Binding !AudioSettings.LameTargetBitrate, Mode=TwoWay}">
|
||||||
|
<TextBlock Text="Quality" />
|
||||||
|
</RadioButton>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</controls:GroupBox>
|
||||||
|
|
||||||
|
<CheckBox HorizontalAlignment="Right" Grid.Column="1" IsChecked="{Binding AudioSettings.LameDownsampleMono, Mode=TwoWay}">
|
||||||
|
<TextBlock Text="Downsample to mono?
(Recommended)" />
|
||||||
|
</CheckBox>
|
||||||
|
</Grid>
|
||||||
|
<controls:GroupBox Margin="5,5,5,0" BorderWidth="1" Label="Bitrate" IsEnabled="{Binding AudioSettings.LameTargetBitrate}" >
|
||||||
|
<StackPanel>
|
||||||
|
<Grid ColumnDefinitions="*,25,Auto">
|
||||||
|
<Slider
|
||||||
|
Grid.Column="0"
|
||||||
|
IsEnabled="{Binding !AudioSettings.LameMatchSource}"
|
||||||
|
Value="{Binding AudioSettings.LameBitrate, Mode=TwoWay}"
|
||||||
|
Minimum="16"
|
||||||
|
Maximum="320"
|
||||||
|
IsSnapToTickEnabled="True" TickFrequency="16"
|
||||||
|
Ticks="16,32,48,64,80,96,112,128,144,160,176,192,208,224,240,256,272,288,304,320"
|
||||||
|
TickPlacement="Outside">
|
||||||
|
<Slider.Styles>
|
||||||
|
<Style Selector="Slider /template/ Thumb">
|
||||||
|
<Setter Property="ToolTip.Tip" Value="{Binding $parent[Slider].Value, Mode=OneWay, StringFormat='\{0:f0\} Kbps'}" />
|
||||||
|
<Setter Property="ToolTip.Placement" Value="Top" />
|
||||||
|
<Setter Property="ToolTip.VerticalOffset" Value="-10" />
|
||||||
|
<Setter Property="ToolTip.HorizontalOffset" Value="-30" />
|
||||||
|
</Style>
|
||||||
|
</Slider.Styles>
|
||||||
|
</Slider>
|
||||||
|
<TextBlock Grid.Column="1" HorizontalAlignment="Right" Text="{Binding AudioSettings.LameBitrate}" />
|
||||||
|
<TextBlock Grid.Column="2" Text=" Kbps" />
|
||||||
|
</Grid>
|
||||||
|
<Grid ColumnDefinitions="Auto,*">
|
||||||
|
|
||||||
|
<CheckBox Grid.Column="0" IsChecked="{Binding AudioSettings.LameConstantBitrate, Mode=TwoWay}">
|
||||||
|
<TextBlock Text="Restrict Encoder to Constant Bitrate?" />
|
||||||
|
</CheckBox>
|
||||||
|
<CheckBox Grid.Column="1" IsChecked="{Binding AudioSettings.LameMatchSource, Mode=TwoWay}" HorizontalAlignment="Right">
|
||||||
|
<TextBlock Text="Match Source Bitrate?" />
|
||||||
|
</CheckBox>
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
</controls:GroupBox>
|
||||||
|
|
||||||
|
<controls:GroupBox Margin="5,5,5,0" BorderWidth="1" Label="Quality" IsEnabled="{Binding !AudioSettings.LameTargetBitrate}" >
|
||||||
|
<Grid ColumnDefinitions="*,*,25" RowDefinitions="*,Auto">
|
||||||
|
<Slider
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Value="{Binding AudioSettings.LameVBRQuality, Mode=TwoWay}"
|
||||||
|
Minimum="0"
|
||||||
|
Maximum="9"
|
||||||
|
IsSnapToTickEnabled="True" TickFrequency="1"
|
||||||
|
Ticks="0,1,2,3,4,5,6,7,8,9"
|
||||||
|
TickPlacement="Outside">
|
||||||
|
<Slider.Styles>
|
||||||
|
<Style Selector="Slider /template/ Thumb">
|
||||||
|
<Setter Property="ToolTip.Tip" Value="{Binding $parent[Slider].Value, Mode=OneWay, StringFormat='V\{0:f0\}'}" />
|
||||||
|
<Setter Property="ToolTip.Placement" Value="Top" />
|
||||||
|
<Setter Property="ToolTip.VerticalOffset" Value="-10" />
|
||||||
|
<Setter Property="ToolTip.HorizontalOffset" Value="-30" />
|
||||||
|
</Style>
|
||||||
|
</Slider.Styles>
|
||||||
|
</Slider>
|
||||||
|
<StackPanel Grid.Column="2" HorizontalAlignment="Right" Orientation="Horizontal">
|
||||||
|
<TextBlock Text="V" />
|
||||||
|
<TextBlock Text="{Binding AudioSettings.LameVBRQuality}" />
|
||||||
|
</StackPanel>
|
||||||
|
<TextBlock Margin="10,0,0,0" Grid.Column="0" Grid.Row="1" Text="Higher" />
|
||||||
|
<TextBlock Margin="0,0,10,0" Grid.Column="1" Grid.Row="1" HorizontalAlignment="Right" Text="Lower" />
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</controls:GroupBox>
|
||||||
|
|
||||||
|
<TextBlock Margin="5,5,5,5" Text="Using L.A.M.E encoding engine" FontStyle="Italic" />
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</controls:GroupBox>
|
||||||
|
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<controls:GroupBox
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Margin="5"
|
||||||
|
BorderWidth="1" IsEnabled="{Binding AudioSettings.SplitFilesByChapter}"
|
||||||
|
Label="{Binding AudioSettings.ChapterTitleTemplateText}">
|
||||||
|
<Grid ColumnDefinitions="*,Auto">
|
||||||
|
<TextBox
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,10,10,10"
|
||||||
|
FontSize="14"
|
||||||
|
IsReadOnly="True"
|
||||||
|
Text="{Binding AudioSettings.ChapterTitleTemplate}" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
Grid.Column="1"
|
||||||
|
Content="Edit"
|
||||||
|
Height="30"
|
||||||
|
Padding="30,3,30,3"
|
||||||
|
Click="EditChapterTitleTemplateButton_Click" />
|
||||||
|
</Grid>
|
||||||
|
</controls:GroupBox>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
<TabItem>
|
||||||
|
|
||||||
|
<TabItem.Header>
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
FontSize="14"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Important Settings"/>
|
||||||
|
|
||||||
|
</TabItem.Header>
|
||||||
|
<Border
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.Row="0"
|
||||||
|
BorderThickness="2"
|
||||||
|
Background="WhiteSmoke"
|
||||||
|
BorderBrush="{DynamicResource DataGridGridLinesBrush}">
|
||||||
|
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
<TabItem>
|
||||||
|
|
||||||
|
<TabItem.Header>
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
FontSize="14"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Import Library"/>
|
||||||
|
|
||||||
|
</TabItem.Header>
|
||||||
|
<Border
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.Row="0"
|
||||||
|
BorderThickness="2"
|
||||||
|
Background="WhiteSmoke"
|
||||||
|
BorderBrush="{DynamicResource DataGridGridLinesBrush}">
|
||||||
|
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
</TabControl>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
@ -0,0 +1,322 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using LibationFileManager;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using ReactiveUI;
|
||||||
|
using Dinah.Core;
|
||||||
|
|
||||||
|
namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
||||||
|
{
|
||||||
|
public partial class SettingsDialog : DialogWindow
|
||||||
|
{
|
||||||
|
private SettingsPages settingsDisp;
|
||||||
|
public SettingsDialog()
|
||||||
|
{
|
||||||
|
if (Design.IsDesignMode)
|
||||||
|
AudibleUtilities.AudibleApiStorage.EnsureAccountsSettingsFileExists();
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
DataContext = settingsDisp = new(Configuration.Instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
protected override async Task SaveAndCloseAsync()
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
await base.SaveAndCloseAsync();
|
||||||
|
}
|
||||||
|
public async void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
=> await SaveAndCloseAsync();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void EditFolderTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var newTemplate = editTemplate(Templates.ChapterTitle, settingsDisp.AudioSettings.ChapterTitleTemplate);
|
||||||
|
if (newTemplate is not null)
|
||||||
|
settingsDisp.AudioSettings.ChapterTitleTemplate = newTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void EditFileTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var newTemplate = editTemplate(Templates.ChapterTitle, settingsDisp.AudioSettings.ChapterTitleTemplate);
|
||||||
|
if (newTemplate is not null)
|
||||||
|
settingsDisp.AudioSettings.ChapterTitleTemplate = newTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void EditChapterFileTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var newTemplate = editTemplate(Templates.ChapterTitle, settingsDisp.AudioSettings.ChapterTitleTemplate);
|
||||||
|
if (newTemplate is not null)
|
||||||
|
settingsDisp.AudioSettings.ChapterTitleTemplate = newTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void EditChapterTitleTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var newTemplate = editTemplate(Templates.ChapterTitle, settingsDisp.AudioSettings.ChapterTitleTemplate);
|
||||||
|
if (newTemplate is not null)
|
||||||
|
settingsDisp.AudioSettings.ChapterTitleTemplate = newTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static string editTemplate(Templates template, string existingTemplate)
|
||||||
|
{
|
||||||
|
var form = new LibationWinForms.Dialogs.EditTemplateDialog(template, existingTemplate);
|
||||||
|
if (form.ShowDialog() == System.Windows.Forms.DialogResult.OK)
|
||||||
|
return form.TemplateText;
|
||||||
|
else return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
internal interface ISettingsTab
|
||||||
|
{
|
||||||
|
void LoadSettings(Configuration config);
|
||||||
|
void SaveSettings(Configuration config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SettingsPages
|
||||||
|
{
|
||||||
|
public Configuration config { get; }
|
||||||
|
public SettingsPages(Configuration config)
|
||||||
|
{
|
||||||
|
this.config = config;
|
||||||
|
AudioSettings = new(config);
|
||||||
|
DownloadDecryptSettings = new(config);
|
||||||
|
}
|
||||||
|
public AudioSettings AudioSettings { get;}
|
||||||
|
public DownloadDecryptSettings DownloadDecryptSettings { get;}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DownloadDecryptSettings : ViewModels.ViewModelBase, ISettingsTab
|
||||||
|
{
|
||||||
|
private static Func<string, string> desc { get; } = Configuration.GetDescription;
|
||||||
|
|
||||||
|
private bool _badBookAsk;
|
||||||
|
private bool _badBookAbort;
|
||||||
|
private bool _badBookRetry;
|
||||||
|
private bool _badBookIgnore;
|
||||||
|
|
||||||
|
private string _folderTemplate;
|
||||||
|
private string _fileTemplate;
|
||||||
|
private string _chapterFileTemplate;
|
||||||
|
|
||||||
|
public DownloadDecryptSettings(Configuration config)
|
||||||
|
{
|
||||||
|
LoadSettings(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadSettings(Configuration config)
|
||||||
|
{
|
||||||
|
BadBookAsk = config.BadBook is Configuration.BadBookAction.Ask;
|
||||||
|
BadBookAbort = config.BadBook is Configuration.BadBookAction.Abort;
|
||||||
|
BadBookRetry = config.BadBook is Configuration.BadBookAction.Retry;
|
||||||
|
BadBookIgnore = config.BadBook is Configuration.BadBookAction.Ignore;
|
||||||
|
FolderTemplate = config.FolderTemplate;
|
||||||
|
FileTemplate = config.FileTemplate;
|
||||||
|
ChapterFileTemplate = config.ChapterFileTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveSettings(Configuration config)
|
||||||
|
{
|
||||||
|
config.BadBook
|
||||||
|
= BadBookAbort ? Configuration.BadBookAction.Abort
|
||||||
|
: BadBookRetry ? Configuration.BadBookAction.Retry
|
||||||
|
: BadBookIgnore ? Configuration.BadBookAction.Ignore
|
||||||
|
: Configuration.BadBookAction.Ask;
|
||||||
|
|
||||||
|
config.FolderTemplate = FolderTemplate;
|
||||||
|
config.FileTemplate = FileTemplate;
|
||||||
|
config.ChapterFileTemplate = ChapterFileTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string BadBookGroupboxText => desc(nameof(Configuration.BadBook));
|
||||||
|
public string BadBookAskText { get; } = Configuration.BadBookAction.Ask.GetDescription();
|
||||||
|
public string BadBookAbortText { get; } = Configuration.BadBookAction.Abort.GetDescription();
|
||||||
|
public string BadBookRetryText { get; } = Configuration.BadBookAction.Retry.GetDescription();
|
||||||
|
public string BadBookIgnoreText { get; } = Configuration.BadBookAction.Ignore.GetDescription();
|
||||||
|
|
||||||
|
public string FolderTemplateText => desc(nameof(Configuration.FolderTemplate));
|
||||||
|
public string FileTemplateText => desc(nameof(Configuration.FileTemplate));
|
||||||
|
public string ChapterFileTemplateText => desc(nameof(Configuration.ChapterFileTemplate));
|
||||||
|
public string? EditCharReplacementText => desc(nameof(Configuration.ReplacementCharacters));
|
||||||
|
|
||||||
|
public string FolderTemplate { get => _folderTemplate; set { this.RaiseAndSetIfChanged(ref _folderTemplate, value); } }
|
||||||
|
public string FileTemplate { get => _fileTemplate; set { this.RaiseAndSetIfChanged(ref _fileTemplate, value); } }
|
||||||
|
public string ChapterFileTemplate { get => _chapterFileTemplate; set { this.RaiseAndSetIfChanged(ref _chapterFileTemplate, value); } }
|
||||||
|
|
||||||
|
|
||||||
|
public bool BadBookAsk
|
||||||
|
{
|
||||||
|
get => _badBookAsk;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
this.RaiseAndSetIfChanged(ref _badBookAsk, value);
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
BadBookAbort = false;
|
||||||
|
BadBookRetry = false;
|
||||||
|
BadBookIgnore = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public bool BadBookAbort
|
||||||
|
{
|
||||||
|
get => _badBookAbort;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
this.RaiseAndSetIfChanged(ref _badBookAbort, value);
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
BadBookAsk = false;
|
||||||
|
BadBookRetry = false;
|
||||||
|
BadBookIgnore = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public bool BadBookRetry
|
||||||
|
{
|
||||||
|
get => _badBookRetry;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
this.RaiseAndSetIfChanged(ref _badBookRetry, value);
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
BadBookAsk = false;
|
||||||
|
BadBookAbort = false;
|
||||||
|
BadBookIgnore = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public bool BadBookIgnore
|
||||||
|
{
|
||||||
|
get => _badBookIgnore;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
this.RaiseAndSetIfChanged(ref _badBookIgnore, value);
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
BadBookAsk = false;
|
||||||
|
BadBookAbort = false;
|
||||||
|
BadBookRetry = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AudioSettings : ViewModels.ViewModelBase, ISettingsTab
|
||||||
|
{
|
||||||
|
|
||||||
|
private bool _splitFilesByChapter;
|
||||||
|
private bool _allowLibationFixup;
|
||||||
|
private bool _lameTargetBitrate;
|
||||||
|
private bool _lameMatchSource;
|
||||||
|
private int _lameBitrate;
|
||||||
|
private int _lameVBRQuality;
|
||||||
|
private string _chapterTitleTemplate;
|
||||||
|
private static Func<string, string> desc { get; } = Configuration.GetDescription;
|
||||||
|
public AudioSettings(Configuration config)
|
||||||
|
{
|
||||||
|
LoadSettings(config);
|
||||||
|
}
|
||||||
|
public void LoadSettings(Configuration config)
|
||||||
|
{
|
||||||
|
CreateCueSheet = config.CreateCueSheet;
|
||||||
|
AllowLibationFixup = config.AllowLibationFixup;
|
||||||
|
DownloadCoverArt = config.DownloadCoverArt;
|
||||||
|
RetainAaxFile = config.RetainAaxFile;
|
||||||
|
SplitFilesByChapter = config.SplitFilesByChapter;
|
||||||
|
MergeOpeningAndEndCredits = config.MergeOpeningAndEndCredits;
|
||||||
|
StripAudibleBrandAudio = config.StripAudibleBrandAudio;
|
||||||
|
StripUnabridged = config.StripUnabridged;
|
||||||
|
ChapterTitleTemplate = config.ChapterTitleTemplate;
|
||||||
|
DecryptToLossy = config.DecryptToLossy;
|
||||||
|
LameTargetBitrate = config.LameTargetBitrate;
|
||||||
|
LameDownsampleMono = config.LameDownsampleMono;
|
||||||
|
LameConstantBitrate = config.LameConstantBitrate;
|
||||||
|
LameMatchSource = config.LameMatchSourceBR;
|
||||||
|
LameBitrate = config.LameBitrate;
|
||||||
|
LameVBRQuality = config.LameVBRQuality;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveSettings(Configuration config)
|
||||||
|
{
|
||||||
|
config.CreateCueSheet = CreateCueSheet;
|
||||||
|
config.AllowLibationFixup = AllowLibationFixup;
|
||||||
|
config.DownloadCoverArt = DownloadCoverArt;
|
||||||
|
config.RetainAaxFile = RetainAaxFile;
|
||||||
|
config.SplitFilesByChapter = SplitFilesByChapter;
|
||||||
|
config.MergeOpeningAndEndCredits = MergeOpeningAndEndCredits;
|
||||||
|
config.StripAudibleBrandAudio = StripAudibleBrandAudio;
|
||||||
|
config.StripUnabridged = StripUnabridged;
|
||||||
|
config.ChapterTitleTemplate = ChapterTitleTemplate;
|
||||||
|
config.DecryptToLossy = DecryptToLossy;
|
||||||
|
config.LameTargetBitrate = LameTargetBitrate;
|
||||||
|
config.LameDownsampleMono = LameDownsampleMono;
|
||||||
|
config.LameConstantBitrate = LameConstantBitrate;
|
||||||
|
config.LameMatchSourceBR = LameMatchSource;
|
||||||
|
config.LameBitrate = LameBitrate;
|
||||||
|
config.LameVBRQuality = LameVBRQuality;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CreateCueSheetText => desc(nameof(Configuration.CreateCueSheet));
|
||||||
|
public string AllowLibationFixupText => desc(nameof(Configuration.AllowLibationFixup));
|
||||||
|
public string DownloadCoverArtText => desc(nameof(Configuration.DownloadCoverArt));
|
||||||
|
public string RetainAaxFileText => desc(nameof(Configuration.RetainAaxFile));
|
||||||
|
public string SplitFilesByChapterText => desc(nameof(Configuration.SplitFilesByChapter));
|
||||||
|
public string MergeOpeningEndCreditsText => desc(nameof(Configuration.MergeOpeningAndEndCredits));
|
||||||
|
public string StripAudibleBrandingText => desc(nameof(Configuration.StripAudibleBrandAudio));
|
||||||
|
public string StripUnabridgedText => desc(nameof(Configuration.StripUnabridged));
|
||||||
|
public string ChapterTitleTemplateText => desc(nameof(Configuration.ChapterTitleTemplate));
|
||||||
|
|
||||||
|
public bool CreateCueSheet { get; set; }
|
||||||
|
public bool DownloadCoverArt { get; set; }
|
||||||
|
public bool RetainAaxFile { get; set; }
|
||||||
|
public bool MergeOpeningAndEndCredits { get; set; }
|
||||||
|
public bool StripAudibleBrandAudio { get; set; }
|
||||||
|
public bool StripUnabridged { get; set; }
|
||||||
|
public bool DecryptToLossy { get; set; }
|
||||||
|
|
||||||
|
public bool LameDownsampleMono { get; set; } = Design.IsDesignMode;
|
||||||
|
public bool LameConstantBitrate { get; set; } = Design.IsDesignMode;
|
||||||
|
|
||||||
|
public bool SplitFilesByChapter { get => _splitFilesByChapter; set { this.RaiseAndSetIfChanged(ref _splitFilesByChapter, value); } }
|
||||||
|
public bool LameTargetBitrate { get => _lameTargetBitrate; set { this.RaiseAndSetIfChanged(ref _lameTargetBitrate, value); } }
|
||||||
|
public bool LameMatchSource { get => _lameMatchSource; set { this.RaiseAndSetIfChanged(ref _lameMatchSource, value); } }
|
||||||
|
public int LameBitrate { get => _lameBitrate; set { this.RaiseAndSetIfChanged(ref _lameBitrate, value); } }
|
||||||
|
public int LameVBRQuality { get => _lameVBRQuality; set { this.RaiseAndSetIfChanged(ref _lameVBRQuality, value); } }
|
||||||
|
|
||||||
|
|
||||||
|
public string ChapterTitleTemplate { get => _chapterTitleTemplate; set { this.RaiseAndSetIfChanged(ref _chapterTitleTemplate, value); } }
|
||||||
|
|
||||||
|
|
||||||
|
public bool AllowLibationFixup
|
||||||
|
{
|
||||||
|
get => _allowLibationFixup;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
this.RaiseAndSetIfChanged(ref _allowLibationFixup, value);
|
||||||
|
if (!_allowLibationFixup)
|
||||||
|
{
|
||||||
|
SplitFilesByChapter = false;
|
||||||
|
StripAudibleBrandAudio = false;
|
||||||
|
StripUnabridged = false;
|
||||||
|
DecryptToLossy = false;
|
||||||
|
this.RaisePropertyChanged(nameof(SplitFilesByChapter));
|
||||||
|
this.RaisePropertyChanged(nameof(StripAudibleBrandAudio));
|
||||||
|
this.RaisePropertyChanged(nameof(StripUnabridged));
|
||||||
|
this.RaisePropertyChanged(nameof(DecryptToLossy));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -67,7 +67,8 @@ namespace LibationWinForms.AvaloniaUI.Views
|
|||||||
|
|
||||||
private async void MainWindow_Opened(object sender, EventArgs e)
|
private async void MainWindow_Opened(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
//var settings = new SettingsDialog();
|
||||||
|
//settings.Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ProductsDisplay_Initialized1(object sender, EventArgs e)
|
public void ProductsDisplay_Initialized1(object sender, EventArgs e)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user