Compare commits

...

16 Commits

Author SHA1 Message Date
Robert
d47a2595b9 incr ver 2025-11-10 22:19:24 -05:00
rmcrackan
55e74db4fb Merge pull request #1419 from Mbucari/master
Bug Fixes and UI Improvement
2025-11-10 22:17:12 -05:00
MBucari
0a171222bc Update Dependency 2025-11-10 19:29:30 -07:00
MBucari
c2093157ca Add dolby atmos logo for spatial audiobooks 2025-11-10 19:28:18 -07:00
MBucari
8e073800cd Fix BookDetailsDialog crash when changing error status 2025-11-10 18:25:59 -07:00
MBucari
1daf07b882 Improve logging 2025-11-10 17:58:48 -07:00
MBucari
27a23a16d6 Update AAXClean 2025-11-10 17:34:17 -07:00
Michael Bucari-Tovo
c878b9fec0 Detect webview crash and disable webview login 2025-11-10 13:14:23 -07:00
rmcrackan
7a01f075ac Merge pull request #1415 from Mbucari/master
Fix minor UI bugs
2025-11-08 18:04:13 -05:00
Michael Bucari-Tovo
23d391485d Update AboutDialog and add recent contributors 2025-11-07 10:35:33 -07:00
Michael Bucari-Tovo
46be532740 Improve SearchSyntaxDialog
- Double-clicking a tag will paste the tage into the search bar
- SearchSyntaxDialog now modeless
2025-11-06 23:53:57 -07:00
Michael Bucari-Tovo
e2fd88d075 Improve ScanAccountsDialog usability 2025-11-06 23:24:17 -07:00
Michael Bucari-Tovo
bb0dea3fa9 Improve EditReplacementChars dialog usability 2025-11-06 22:49:09 -07:00
Michael Bucari-Tovo
def0b1f611 Prevent crash if watched RootDirectory is deleted 2025-11-06 14:57:54 -07:00
Michael Bucari-Tovo
bfee579719 Fix DirectoryOrCustomSelectControl 2025-11-06 13:47:51 -07:00
Mbucari
d4139861f3 Only allow mocking lobby bugging 2025-11-06 07:59:55 -07:00
37 changed files with 889 additions and 677 deletions

View File

@@ -13,7 +13,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AAXClean.Codecs" Version="2.0.2.2" />
<PackageReference Include="AAXClean.Codecs" Version="2.0.3.1" />
</ItemGroup>
<ItemGroup>

View File

@@ -2,7 +2,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Version>12.7.0.1</Version>
<Version>12.7.1.1</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Octokit" Version="14.0.0" />

View File

@@ -4,6 +4,7 @@ using AudibleApi.Common;
using AudibleUtilities.Widevine;
using DataLayer;
using Dinah.Core;
using DocumentFormat.OpenXml.Wordprocessing;
using LibationFileManager;
using NAudio.Lame;
using System;
@@ -28,11 +29,7 @@ public partial class DownloadOptions
Serilog.Log.Logger.Debug("Content License {@License}", new
{
license.DrmType,
license.ContentMetadata.ContentReference.Codec,
license.ContentMetadata.ContentReference.Marketplace,
license.ContentMetadata.ContentReference.ContentSizeInBytes,
license.ContentMetadata.ContentReference.Version,
license.ContentMetadata.ContentReference.FileVersion
license.ContentMetadata.ContentReference
});
token.ThrowIfCancellationRequested();
@@ -47,7 +44,24 @@ public partial class DownloadOptions
license.ContentMetadata.ContentReference.Acr,
license.ContentMetadata.ContentReference.FileVersion);
if (metadata is not null && metadata.ContentReference == license.ContentMetadata.ContentReference)
if (metadata is null)
{
Serilog.Log.Logger.Warning("Unable to retrieve metadata for {@FileReference}", new
{
libraryBook.Book.AudibleProductId,
license.DrmType,
license.ContentMetadata.ContentReference.Acr,
license.ContentMetadata.ContentReference.FileVersion
});
}
else if (metadata.ContentReference != license.ContentMetadata.ContentReference)
{
Serilog.Log.Logger.Warning("Metadata ContentReference does not match License ContentReference with drm_type = {@DrmType}. {@Metadata}. {@License} ",
license.DrmType,
metadata.ContentReference,
license.ContentMetadata.ContentReference);
}
else
license.ContentMetadata.ChapterInfo = metadata.ChapterInfo;
token.ThrowIfCancellationRequested();
@@ -72,7 +86,7 @@ public partial class DownloadOptions
private static async Task<LicenseInfo> ChooseContent(Api api, LibraryBook libraryBook, Configuration config, CancellationToken token)
{
Serilog.Log.Logger.Debug("Download Settings {@Settings}", new
Serilog.Log.Logger.Information("Download Settings {@Settings}", new
{
config.FileDownloadQuality,
config.UseWidevine,
@@ -89,9 +103,9 @@ public partial class DownloadOptions
if (config.UseWidevine)
{
if (canUseWidevine)
Serilog.Log.Logger.Information("Unable to get a Widevine CDM. Falling back to ADRM.");
Serilog.Log.Logger.Warning("Unable to get a Widevine CDM. Falling back to ADRM.");
else
Serilog.Log.Logger.Information("Account {@account} is not registered as an android device, so content will not be downloaded with Widevine DRM. Remove and re-add the account in Libation to fix.", libraryBook.Account.ToMask());
Serilog.Log.Logger.Warning("Account {@account} is not registered as an android device, so content will not be downloaded with Widevine DRM. Remove and re-add the account in Libation to fix.", libraryBook.Account.ToMask());
}
token.ThrowIfCancellationRequested();

View File

@@ -14,7 +14,7 @@ namespace FileManager
/// </summary>
public class BackgroundFileSystem : IDisposable
{
public LongPath RootDirectory { get; private set; }
public LongPath? RootDirectory { get; private set; }
public string SearchPattern { get; private set; }
public SearchOption SearchOption { get; private set; }
@@ -51,7 +51,8 @@ namespace FileManager
lock (fsCacheLocker)
{
fsCache.Clear();
fsCache.AddRange(SafestEnumerateFiles(RootDirectory));
if (Directory.Exists(RootDirectory))
fsCache.AddRange(SafestEnumerateFiles(RootDirectory));
}
}
@@ -60,7 +61,14 @@ namespace FileManager
Stop();
lock (fsCacheLocker)
fsCache.AddRange(SafestEnumerateFiles(RootDirectory));
{
if (!Directory.Exists(RootDirectory))
{
RootDirectory = null;
return;
}
fsCache.AddRange(SafestEnumerateFiles(RootDirectory));
}
directoryChangesEvents = new BlockingCollection<FileSystemEventArgs>();
fileSystemWatcher = new FileSystemWatcher(RootDirectory)

View File

@@ -222,6 +222,7 @@ public class App : Application
// logging is init'd here
AppScaffolding.LibationScaffolding.RunPostMigrationScaffolding(AppScaffolding.Variety.Chardonnay, config);
Program.LoggingEnabled = true;
}
private static void ShowMainWindow(IClassicDesktopStyleApplicationLifetime desktop)

View File

@@ -117,6 +117,52 @@
a 168,305 -35 0 0 104,-136
</StreamGeometry>
<StreamGeometry x:Key="DolbyAtmosLogoVertical">
M261.017,370.954h-13.752l38.363-88.449h11.241l37.967,88.449h-13.984l-8.988-21.733h-41.977
L261.017,370.954z M274.257,338.352h33.109l-16.497-41.484L274.257,338.352z M390.748,293.373h28.364v-10.868h-69.087v10.868h28.353
v77.581h12.37V293.373z M472.258,282.505h-19.229v88.449h11.985v-73.959h0.246l29.236,73.959h7.87l29.354-73.959h0.255v73.959
h12.368v-88.449h-19.229l-26.12,67.955h-0.257L472.258,282.505z M668.11,326.61c0,6.502-1.138,12.46-3.394,17.883
c-2.253,5.425-5.369,10.094-9.316,14.018c-3.966,3.92-8.678,6.966-14.135,9.146c-5.477,2.169-11.411,3.255-17.824,3.255
s-12.337-1.086-17.751-3.255c-5.434-2.181-10.114-5.227-14.08-9.146c-3.968-3.924-7.041-8.593-9.266-14.018
c-2.222-5.423-3.328-11.381-3.328-17.883c0-6.567,1.106-12.567,3.328-17.99c2.225-5.425,5.298-10.051,9.266-13.901
c3.966-3.838,8.646-6.826,14.08-8.971c5.414-2.132,11.338-3.2,17.751-3.2s12.348,1.068,17.824,3.2
c5.457,2.145,10.169,5.133,14.135,8.971c3.947,3.851,7.063,8.477,9.316,13.901C666.973,314.043,668.11,320.043,668.11,326.61
M655.4,326.61c0-4.595-0.765-8.919-2.254-13.003c-1.522-4.073-3.647-7.667-6.424-10.752c-2.776-3.089-6.116-5.52-10.039-7.308
c-3.914-1.774-8.34-2.669-13.242-2.669c-4.828,0-9.21,0.895-13.124,2.669c-3.913,1.788-7.253,4.219-9.976,7.308
c-2.734,3.085-4.851,6.679-6.359,10.752c-1.5,4.084-2.256,8.408-2.256,13.003c0,4.674,0.756,9.071,2.256,13.188
c1.509,4.115,3.647,7.698,6.413,10.752c2.773,3.047,6.104,5.445,9.974,7.185c3.883,1.743,8.244,2.615,13.072,2.615
s9.221-0.872,13.178-2.615c3.967-1.739,7.327-4.138,10.104-7.185c2.776-3.054,4.901-6.637,6.424-10.752
C654.636,335.682,655.4,331.284,655.4,326.61 M751.896,292.173c-2.606-2.931-6.063-5.26-10.327-7.003
c-4.276-1.739-8.87-2.612-13.771-2.612c-3.479,0-6.945,0.457-10.403,1.361c-3.436,0.915-6.529,2.361-9.252,4.334
c-2.734,1.984-4.956,4.478-6.659,7.481c-1.701,3.016-2.542,6.611-2.542,10.817c0,3.877,0.629,7.12,1.894,9.726
c1.266,2.611,2.926,4.813,4.989,6.6c2.052,1.771,4.401,3.244,7.008,4.387c2.606,1.144,5.265,2.123,7.955,2.92
c2.691,0.861,5.244,1.706,7.658,2.547c2.415,0.829,4.541,1.84,6.349,3.025c1.831,1.191,3.266,2.638,4.339,4.335
c1.075,1.702,1.607,3.823,1.607,6.349c0,2.542-0.521,4.695-1.554,6.472c-1.021,1.787-2.35,3.266-3.966,4.457
c-1.627,1.185-3.436,2.057-5.402,2.611c-1.989,0.558-3.968,0.829-5.935,0.829c-3.895,0-7.488-0.904-10.817-2.723
c-3.328-1.819-5.977-4.196-7.955-7.126l-9.146,7.71c3.243,4.042,7.338,7.094,12.282,9.152c4.958,2.057,10.073,3.078,15.391,3.078
c3.722,0,7.327-0.51,10.859-1.531c3.531-1.037,6.637-2.595,9.328-4.696c2.688-2.099,4.849-4.747,6.476-7.96
c1.607-3.2,2.425-6.981,2.425-11.337c0-4.196-0.744-7.657-2.255-10.39c-1.498-2.734-3.434-5-5.816-6.829
c-2.372-1.818-5.032-3.275-7.955-4.393c-2.936-1.106-5.819-2.101-8.669-2.966c-2.383-0.799-4.604-1.575-6.711-2.325
c-2.105-0.74-3.925-1.654-5.456-2.723c-1.543-1.068-2.776-2.383-3.69-3.919c-0.903-1.548-1.361-3.462-1.361-5.765
c0-2.371,0.489-4.408,1.478-6.115c0.99-1.701,2.277-3.128,3.862-4.27c1.585-1.145,3.349-1.984,5.284-2.495
c1.937-0.521,3.86-0.775,5.766-0.775c3.563,0,6.764,0.733,9.613,2.201c2.851,1.457,5.105,3.345,6.775,5.631L751.896,292.173z
M0,194.145h28.652c53.454,0,97.049-43.594,97.049-97.068c0-53.481-43.595-97.065-97.049-97.065H0V194.145z M276.172,0.011h-28.641
c-53.476,0-97.061,43.584-97.061,97.065c0,53.475,43.584,97.068,97.061,97.068h28.641V0.011z M405.074,0h-70.108v194.145h70.108
c53.517,0,97.069-43.552,97.069-97.068C502.144,43.552,458.591,0,405.074,0 M405.063,164.711h-19.952h-20.729V29.434h20.729h19.952
c37.268,0,67.641,30.375,67.641,67.643C472.704,134.336,442.331,164.711,405.063,164.711 M584.346,59.797
c-37.106,0-67.27,30.168-67.27,67.265c0,37.102,30.163,67.269,67.27,67.269c37.095,0,67.259-30.167,67.259-67.269
C651.604,89.965,621.44,59.797,584.346,59.797 M584.346,167.376c-22.506,0-40.554-18.305-40.554-40.56
c0-22.51,18.294-40.553,40.554-40.553c22.248,0,40.553,18.294,40.553,40.553C624.898,149.322,606.594,167.376,584.346,167.376
M670.643,194.374h29.428V0.031h-29.428V194.374z M792.759,59.809c-14.295,0-27.546,4.488-38.459,12.124V0.031h-29.491l0.01,194.343
H754.3v-12.161c10.913,7.63,24.164,12.129,38.459,12.129c37.095,0,67.278-30.179,67.278-67.271
C860.037,89.977,829.854,59.809,792.759,59.809 M792.759,167.376c-17.985,0-33.119-11.704-38.459-27.78
c-1.339-4.021-2.095-8.312-2.095-12.768c0-4.483,0.756-8.785,2.095-12.806c5.383-16.171,20.634-27.759,38.459-27.759
c22.259,0,40.562,18.305,40.562,40.564C833.32,149.333,815.018,167.376,792.759,167.376 M967.85,59.84l-38.329,86.201L891.169,59.84
h-32.151l54.41,122.304l-1.084,2.376l-0.385,0.846l-11.782,26.61l-0.075,0.207c-3.53,7.907-12.836,11.486-20.729,7.961l-4.223-1.872
l-8.222,18.469l-3.657,8.188h0.01l0.044,0.021l10.188,4.541c19.133,8.541,41.7-0.143,50.262-19.318
c0.076-0.164,69.684-155.714,76.225-170.333H967.85z
</StreamGeometry>
</ResourceDictionary>
</Styles.Resources>
</Styles>

View File

@@ -0,0 +1,25 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace LibationAvalonia.Controls;
internal class DataGridTextColumnExt : DataGridTextColumn
{
public static readonly StyledProperty<int> MaxLengthProperty =
AvaloniaProperty.Register<DataGridTextColumnExt, int>(nameof(MaxLength));
public int MaxLength
{
get => GetValue(MaxLengthProperty);
set => SetValue(MaxLengthProperty, value);
}
protected override object PrepareCellForEdit(Control editingElement, RoutedEventArgs editingEventArgs)
{
if (editingElement is TextBox textBox)
{
textBox.MaxLength = MaxLength;
}
return base.PrepareCellForEdit(editingElement, editingEventArgs);
}
}

View File

@@ -6,29 +6,63 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="LibationAvalonia.Controls.DirectoryOrCustomSelectControl">
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto" Name="grid">
<controls:DirectorySelectControl
Grid.Column="1"
Grid.Row="0"
IsEnabled="{Binding KnownChecked}"
SelectedDirectory="{Binding SelectedDirectory, Mode=TwoWay}"
SubDirectory="{Binding $parent[1].SubDirectory}"
KnownDirectories="{Binding $parent[1].KnownDirectories}" />
<Grid
RowDefinitions="Auto,Auto,Auto"
ColumnDefinitions="Auto,*,Auto">
<RadioButton
Grid.Column="0"
Grid.Row="0"
IsChecked="{Binding KnownChecked, Mode=TwoWay}"/>
<RadioButton
Grid.RowSpan="2"
Name="rbKnown" />
<RadioButton
Grid.Column="0"
Grid.Row="1"
IsChecked="{Binding CustomChecked, Mode=TwoWay}"/>
<TextBlock
Grid.Column="1"
Grid.ColumnSpan="2"
VerticalAlignment="Center"
Margin="10,0"
IsEnabled="False"
IsVisible="{Binding #cmbKnownDirs.SelectedItem, Converter={x:Static ObjectConverters.IsNull}}"
Text="Select Known Directory:" />
<Grid Grid.Column="1" Grid.Row="1" ColumnDefinitions="*,Auto"
IsEnabled="{Binding CustomChecked}">
<TextBox Grid.Column="0" IsReadOnly="True" Text="{Binding CustomDir, Mode=TwoWay}" />
<Button Grid.Column="1" Content="..." Margin="5,0,0,0" Padding="10,0,10,0" Click="CustomDirBrowseBtn_Click" VerticalAlignment="Stretch" />
</Grid>
</Grid>
<controls:WheelComboBox
Grid.Column="1"
Grid.ColumnSpan="2"
HorizontalAlignment="Stretch"
Margin="0,0,0,3"
IsEnabled="{Binding #rbKnown.IsChecked}"
Name="cmbKnownDirs" />
<TextBox
Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="2"
IsReadOnly="True"
Margin="0,0,0,8"
Name="tboxKnownDirPath"
IsEnabled="{Binding #rbKnown.IsChecked}"
Text="{Binding #cmbKnownDirs.SelectedItem.Directory}" />
<RadioButton
Grid.Row="2"
Name="rbCustom" />
<TextBox
Grid.Row="2"
Grid.Column="1"
HorizontalAlignment="Stretch"
Name="tboxCustomDirPath"
Margin="0,0,10,0"
VerticalAlignment="Center"
Text="{Binding $parent[1].Directory, Mode=OneWayToSource}"
IsEnabled="{Binding #rbCustom.IsChecked}"/>
<Button
Grid.Row="2"
Grid.Column="2"
Name="btnBrowse"
IsEnabled="{Binding #rbCustom.IsChecked}">
<TextBlock Text="..." />
</Button>
</Grid>
</UserControl>

View File

@@ -1,142 +1,184 @@
using Avalonia;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Platform.Storage;
using Dinah.Core;
using LibationFileManager;
using ReactiveUI;
using System.Collections.Generic;
using System.IO;
using System.Linq;
#nullable enable
namespace LibationAvalonia.Controls
{
public partial class DirectoryOrCustomSelectControl : UserControl
{
public static readonly StyledProperty<List<Configuration.KnownDirectories>> KnownDirectoriesProperty =
AvaloniaProperty.Register<DirectorySelectControl, List<Configuration.KnownDirectories>>(nameof(KnownDirectories), DirectorySelectControl.DefaultKnownDirectories);
public static readonly StyledProperty<IList<Configuration.KnownDirectories>?> KnownDirectoriesProperty =
AvaloniaProperty.Register<DirectoryOrCustomSelectControl, IList<Configuration.KnownDirectories>?>(nameof(KnownDirectories), DefaultKnownDirectories);
public static readonly StyledProperty<string> SubDirectoryProperty =
AvaloniaProperty.Register<DirectorySelectControl, string>(nameof(SubDirectory));
public static readonly StyledProperty<string?> SubDirectoryProperty =
AvaloniaProperty.Register<DirectoryOrCustomSelectControl, string?>(nameof(SubDirectory));
public static readonly StyledProperty<string> DirectoryProperty =
AvaloniaProperty.Register<DirectorySelectControl, string>(nameof(Directory));
public static readonly StyledProperty<string?> DirectoryProperty =
AvaloniaProperty.Register<DirectoryOrCustomSelectControl, string?>(nameof(Directory));
public List<Configuration.KnownDirectories> KnownDirectories
public IList<Configuration.KnownDirectories>? KnownDirectories
{
get => GetValue(KnownDirectoriesProperty);
set => SetValue(KnownDirectoriesProperty, value);
}
public string Directory
public string? Directory
{
get => GetValue(DirectoryProperty);
set => SetValue(DirectoryProperty, value);
}
public string SubDirectory
public string? SubDirectory
{
get => GetValue(SubDirectoryProperty);
set => SetValue(SubDirectoryProperty, value);
}
private readonly DirectoryState directoryState = new();
public static IList<Configuration.KnownDirectories> DefaultKnownDirectories => [
Configuration.KnownDirectories.WinTemp,
Configuration.KnownDirectories.UserProfile,
Configuration.KnownDirectories.ApplicationData,
Configuration.KnownDirectories.AppDir,
Configuration.KnownDirectories.MyMusic,
Configuration.KnownDirectories.MyDocs,
Configuration.KnownDirectories.LibationFiles];
private readonly AvaloniaList<KnownDirectoryItem> _knownDirNames;
public DirectoryOrCustomSelectControl()
{
InitializeComponent();
grid.DataContext = directoryState;
directoryState.PropertyChanged += DirectoryState_PropertyChanged;
PropertyChanged += DirectoryOrCustomSelectControl_PropertyChanged;
_knownDirNames = new(GetKnownDirectories(DefaultKnownDirectories));
cmbKnownDirs.ItemsSource = _knownDirNames;
cmbKnownDirs.SelectionChanged += CmbKnownDirs_SelectionChanged;
btnBrowse.Click += Browse_Click;
}
private void DirectoryState_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
private void CmbKnownDirs_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (e.PropertyName is nameof(DirectoryState.SelectedDirectory) or nameof(DirectoryState.KnownChecked) &&
directoryState.KnownChecked &&
directoryState.SelectedDirectory is Configuration.KnownDirectories kdir &&
kdir is not Configuration.KnownDirectories.None)
if (cmbKnownDirs.SelectedItem is KnownDirectoryItem item && item.Directory is not null)
{
Directory = kdir is Configuration.KnownDirectories.AppDir ? Configuration.AppDir_Absolute : Configuration.GetKnownDirectoryPath(kdir);
}
else if (e.PropertyName is nameof(DirectoryState.CustomDir) or nameof(DirectoryState.CustomChecked) &&
directoryState.CustomChecked &&
directoryState.CustomDir is not null)
{
Directory = directoryState.CustomDir;
Directory = item.Directory;
}
}
private class DirectoryState : ViewModels.ViewModelBase
{
private string _customDir;
private string _subDirectory;
private bool _knownChecked;
private bool _customChecked;
private Configuration.KnownDirectories? _selectedDirectory;
public string CustomDir { get => _customDir; set => this.RaiseAndSetIfChanged(ref _customDir, value); }
public string SubDirectory { get => _subDirectory; set => this.RaiseAndSetIfChanged(ref _subDirectory, value); }
public bool KnownChecked { get => _knownChecked; set => this.RaiseAndSetIfChanged(ref _knownChecked, value); }
public bool CustomChecked { get => _customChecked; set => this.RaiseAndSetIfChanged(ref _customChecked, value); }
private IEnumerable<KnownDirectoryItem> GetKnownDirectories(IEnumerable<Configuration.KnownDirectories> knownDirs)
=> knownDirs.Select(k => new KnownDirectoryItem(k, SubDirectory)).Where(k => k.Directory is not null);
public Configuration.KnownDirectories? SelectedDirectory { get => _selectedDirectory; set => this.RaiseAndSetIfChanged(ref _selectedDirectory, value); }
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
if (change.Property == SubDirectoryProperty)
{
foreach (var item in _knownDirNames)
{
item.SubDirectory = SubDirectory;
}
VerifyAndApplyDirectory(Directory);
}
else if (change.Property == KnownDirectoriesProperty)
{
var knownDirs = KnownDirectories?.Count > 0 ? KnownDirectories : DefaultKnownDirectories;
if (!_knownDirNames.Select(k => k.KnownDirectory).SequenceEqual(knownDirs))
{
_knownDirNames.Clear();
_knownDirNames.AddRange(GetKnownDirectories(knownDirs));
}
VerifyAndApplyDirectory(Directory);
}
else if (change.Property == DirectoryProperty)
{
VerifyAndApplyDirectory(Directory);
}
base.OnPropertyChanged(change);
}
private async void CustomDirBrowseBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
private void VerifyAndApplyDirectory(string? directory)
{
var options = new Avalonia.Platform.Storage.FolderPickerOpenOptions
if (string.IsNullOrWhiteSpace(Directory))
return;
bool dirIsKnown = false;
foreach (var item in _knownDirNames)
{
if (item.IsSamePathAs(directory))
{
rbKnown.IsChecked = true;
Directory = item.Directory;
cmbKnownDirs.SelectedItem = item;
dirIsKnown = true;
break;
}
}
if (!dirIsKnown)
{
tboxCustomDirPath.Text = directory;
rbCustom.IsChecked = true;
}
}
public async void Browse_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{
if (VisualRoot is not Window window)
return;
var options = new FolderPickerOpenOptions
{
AllowMultiple = false
};
var selectedFolders = await (VisualRoot as Window).StorageProvider.OpenFolderPickerAsync(options);
directoryState.CustomDir = selectedFolders.SingleOrDefault()?.TryGetLocalPath() ?? directoryState.CustomDir;
var selectedFolders = await window.StorageProvider.OpenFolderPickerAsync(options);
Directory = selectedFolders.SingleOrDefault()?.TryGetLocalPath() ?? Directory;
}
private void DirectoryOrCustomSelectControl_PropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
private class KnownDirectoryItem : ReactiveObject
{
if (e.Property == DirectoryProperty)
public Configuration.KnownDirectories KnownDirectory { get; set; }
private string? _directory;
public string? Directory { get => _directory; private set => this.RaiseAndSetIfChanged(ref _directory, value); }
public string? Name { get; }
private string? _subDir;
public string? SubDirectory
{
var directory = Directory?.Trim() ?? "";
var noSubDir = RemoveSubDirectoryFromPath(directory);
var known = Configuration.GetKnownDirectory(noSubDir);
if (known == Configuration.KnownDirectories.None && noSubDir == Configuration.AppDir_Absolute)
known = Configuration.KnownDirectories.AppDir;
if (known is Configuration.KnownDirectories.None)
get => _subDir;
set
{
directoryState.CustomDir = directory;
directoryState.CustomChecked = true;
}
else
{
directoryState.SelectedDirectory = known;
directoryState.KnownChecked = true;
_subDir = value;
if (Configuration.GetKnownDirectoryPath(KnownDirectory) is string dir)
{
Directory = Path.Combine(dir, _subDir ?? "");
}
}
}
else if (e.Property == KnownDirectoriesProperty &&
KnownDirectories.Count > 0 &&
directoryState.SelectedDirectory is null or Configuration.KnownDirectories.None)
directoryState.SelectedDirectory = KnownDirectories[0];
}
private string RemoveSubDirectoryFromPath(string path)
{
if (string.IsNullOrWhiteSpace(SubDirectory))
return path;
public KnownDirectoryItem(Configuration.KnownDirectories known, string? subDir)
{
Name = known.GetDescription();
KnownDirectory = known;
SubDirectory = subDir;
}
path = path?.Trim() ?? "";
if (string.IsNullOrWhiteSpace(path))
return path;
public bool IsSamePathAs(string? otherPath)
{
if (string.IsNullOrWhiteSpace(otherPath) || string.IsNullOrWhiteSpace(Directory))
return false;
var bottomDir = System.IO.Path.GetFileName(path);
if (SubDirectory.EqualsInsensitive(bottomDir))
return System.IO.Path.GetDirectoryName(path);
try
{
var p1 = Path.GetFullPath(Directory);
var p2 = Path.GetFullPath(otherPath);
return p1.Equals(p2, System.StringComparison.OrdinalIgnoreCase);
}
catch { return false; }
}
return path;
public override string? ToString() => Name?.ToString();
}
}
}

View File

@@ -8,7 +8,7 @@
x:DataType="vm:ImportantSettingsVM"
x:Class="LibationAvalonia.Controls.Settings.Important">
<Grid RowDefinitions="Auto,Auto,Auto,*">
<Grid RowDefinitions="Auto,Auto,Auto,Auto,*">
<controls:GroupBox
Grid.Row="0"
Margin="5"
@@ -69,9 +69,16 @@
</StackPanel>
</controls:GroupBox>
<CheckBox
Grid.Row="1"
Margin="10,5"
IsChecked="{CompiledBinding UseWebView, Mode=TwoWay}">
<TextBlock Text="{CompiledBinding UseWebViewText}" />
</CheckBox>
<StackPanel
Grid.Row="1" Margin="5"
Grid.Row="2" Margin="5"
Orientation="Horizontal">
<TextBlock
@@ -96,7 +103,7 @@
</StackPanel>
<controls:GroupBox
Grid.Row="2"
Grid.Row="3"
Margin="5"
Label="Display Settings">
<Grid
@@ -151,7 +158,7 @@
</controls:GroupBox>
<Grid
Grid.Row="3"
Grid.Row="4"
ColumnDefinitions="Auto,Auto,*"
Margin="10"
VerticalAlignment="Bottom">

View File

@@ -9,15 +9,15 @@
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
Title="About Libation">
<Grid Margin="10" ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto,Auto,*">
<Grid Margin="10" RowDefinitions="Auto,Auto,Auto,Auto,*">
<controls:LinkLabel Grid.ColumnSpan="2" FontSize="16" FontWeight="Bold" Text="{Binding Version}" ToolTip.Tip="View Release Notes" Tapped="ViewReleaseNotes_Tapped" />
<controls:LinkLabel FontSize="16" FontWeight="Bold" Text="{Binding Version}" ToolTip.Tip="View Release Notes" Tapped="ViewReleaseNotes_Tapped" />
<controls:LinkLabel Grid.Column="1" FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Right" Text="https://getlibation.com" Tapped="Link_getlibation"/>
<controls:LinkLabel Grid.Row="1" FontSize="14" VerticalAlignment="Center" Text="https://getlibation.com" Tapped="Link_getlibation"/>
<Button Grid.Row="1" Grid.ColumnSpan="2" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" Margin="0,20,0,0" IsEnabled="{Binding CanCheckForUpgrade}" Content="{Binding UpgradeButtonText}" Click="CheckForUpgrade_Click" />
<Button Grid.Row="2" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" Margin="0,10,0,0" IsEnabled="{Binding CanCheckForUpgrade}" Content="{Binding UpgradeButtonText}" Click="CheckForUpgrade_Click" />
<Canvas Grid.Row="2" Grid.ColumnSpan="2" Margin="0,30,0,20" Width="280" Height="220">
<Canvas Grid.Row="3" Margin="0,30,0,20" Width="280" Height="220">
<Path Stretch="None" Fill="{DynamicResource IconFill}" Data="{DynamicResource LibationCheersIcon}">
<Path.RenderTransform>
<TransformGroup>
@@ -39,7 +39,7 @@
</Path>
</Canvas>
<controls:GroupBox Grid.Row="3" Label="Acknowledgements" Grid.ColumnSpan="2">
<controls:GroupBox Grid.Row="4" Label="Acknowledgements">
<StackPanel>
<StackPanel.Styles>
<Style Selector="controls|LinkLabel">

View File

@@ -7,6 +7,9 @@
Width="650" Height="500"
x:Class="LibationAvalonia.Dialogs.BookDetailsDialog"
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
xmlns:dialogs="clr-namespace:LibationAvalonia.Dialogs"
x:DataType="dialogs:BookDetailsDialog+BookDetailsDialogViewModel"
x:CompileBindings="True"
Title="Book Details" Name="BookDetails">
<Grid RowDefinitions="*,Auto,Auto,40">
@@ -16,27 +19,32 @@
<Setter Property="BorderThickness" Value="2" />
</Style>
</Grid.Styles>
<Grid ColumnDefinitions="Auto,*" RowDefinitions="*,Auto" Margin="10,10,10,0">
<Panel VerticalAlignment="Top" Margin="5" Background="LightGray" Width="80" Height="80" >
<Image Grid.Column="0" Width="80" Height="80" Source="{Binding Cover}" />
</Panel>
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,*,Auto" Margin="10">
<Image Source="{Binding Cover}" />
<Panel Grid.Column="0" Grid.Row="1">
<Path
Grid.Row="1"
VerticalAlignment="Center"
Stretch="Uniform"
Width="80"
Fill="{DynamicResource IconFill}"
IsVisible="{Binding IsSpatial}"
Data="{StaticResource DolbyAtmosLogoVertical}" />
<controls:LinkLabel
Margin="10"
TextWrapping="Wrap"
TextAlignment="Center"
Tapped="GoToAudible_Tapped"
Text="Open in&#xa;Audible&#xa;(Browser)" />
</Panel>
<controls:LinkLabel
Grid.Row="2"
HorizontalAlignment="Center"
TextAlignment="Center"
VerticalAlignment="Bottom"
TextWrapping="Wrap"
Command="{Binding OpenInAudibleCommand}"
Text="Open in &#xa;Audible&#xa;(Browser)" />
<TextBox
Grid.Column="1"
Grid.Row="0"
Grid.RowSpan="2"
Grid.RowSpan="3"
Margin="10,0,0,0"
TextWrapping="Wrap"
Margin="5"
FontSize="12"
Text="{Binding DetailsText}" />
</Grid>
@@ -91,6 +99,7 @@
MinHeight="25"
Height="25"
VerticalAlignment="Center"
SelectionChanged="BookStatus_SelectionChanged"
SelectedItem="{Binding BookLiberatedSelectedItem, Mode=TwoWay}"
ItemsSource="{Binding BookLiberatedItems}">

View File

@@ -1,13 +1,16 @@
using ApplicationServices;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Media.Imaging;
using DataLayer;
using Dinah.Core;
using LibationAvalonia.Controls;
using LibationAvalonia.ViewModels;
using LibationFileManager;
using ReactiveUI;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Input;
namespace LibationAvalonia.Dialogs
{
@@ -40,7 +43,7 @@ namespace LibationAvalonia.Dialogs
MainVM.Configure_NonUI();
LibraryBook
= MockLibraryBook
.CreateBook()
.CreateBook(isSpatial: true)
.AddAuthor("Author 2")
.AddNarrator("Narrator 2")
.AddSeries("Series Name", 1)
@@ -61,53 +64,47 @@ namespace LibationAvalonia.Dialogs
base.SaveAndClose();
}
public void GoToAudible_Tapped(object sender, Avalonia.Input.TappedEventArgs e)
public void BookStatus_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var locale = AudibleApi.Localization.Get(_libraryBook.Book.Locale);
var link = $"https://www.audible.{locale.TopDomain}/pd/{_libraryBook.Book.AudibleProductId}";
Go.To.Url(link);
if (sender is not WheelComboBox { SelectedItem: liberatedComboBoxItem { Status: LiberatedStatus.Error } } &&
_viewModel.BookLiberatedItems.SingleOrDefault(s => s.Status == LiberatedStatus.Error) is liberatedComboBoxItem errorItem)
{
_viewModel.BookLiberatedItems.Remove(errorItem);
}
}
public void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
=> SaveAndClose();
private class BookDetailsDialogViewModel : ViewModelBase
public class liberatedComboBoxItem
{
public class liberatedComboBoxItem
{
public LiberatedStatus Status { get; set; }
public string Text { get; set; }
public override string ToString() => Text;
}
public LiberatedStatus Status { get; set; }
public string Text { get; set; }
public override string ToString() => Text;
}
public class BookDetailsDialogViewModel : ViewModelBase
{
public Bitmap Cover { get; set; }
public string DetailsText { get; set; }
public string Tags { get; set; }
public bool IsSpatial { get; }
public bool HasPDF => PdfLiberatedItems?.Count > 0;
private liberatedComboBoxItem _bookLiberatedSelectedItem;
public ObservableCollection<liberatedComboBoxItem> BookLiberatedItems { get; } = new();
public AvaloniaList<liberatedComboBoxItem> BookLiberatedItems { get; } = new();
public List<liberatedComboBoxItem> PdfLiberatedItems { get; } = new();
public liberatedComboBoxItem PdfLiberatedSelectedItem { get; set; }
public liberatedComboBoxItem BookLiberatedSelectedItem
{
get => _bookLiberatedSelectedItem;
set
{
_bookLiberatedSelectedItem = value;
if (value?.Status is not LiberatedStatus.Error)
{
BookLiberatedItems.Remove(BookLiberatedItems.SingleOrDefault(s => s.Status == LiberatedStatus.Error));
}
}
}
public liberatedComboBoxItem BookLiberatedSelectedItem { get; set; }
public ICommand OpenInAudibleCommand { get; }
public BookDetailsDialogViewModel(LibraryBook libraryBook)
{
var Book = libraryBook.Book;
var locale = AudibleApi.Localization.Get(libraryBook.Book.Locale);
var link = $"https://www.audible.{locale.TopDomain}/pd/{libraryBook.Book.AudibleProductId}";
OpenInAudibleCommand = ReactiveCommand.Create(() => Go.To.Url(link));
IsSpatial = libraryBook.Book.IsSpatial;
//init tags
Tags = libraryBook.Book.UserDefinedItem.Tags;

View File

@@ -2,92 +2,80 @@
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="500" d:DesignHeight="450"
MinWidth="500" MinHeight="450"
Width="500" Height="450"
mc:Ignorable="d" d:DesignWidth="450" d:DesignHeight="450"
MinWidth="450" MinHeight="450"
Width="450" Height="450"
x:Class="LibationAvalonia.Dialogs.EditReplacementChars"
xmlns:dialogs="clr-namespace:LibationAvalonia.Dialogs"
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
x:DataType="dialogs:EditReplacementChars"
Title="Illegal Character Replacement">
<Grid
RowDefinitions="*,Auto"
ColumnDefinitions="*,Auto">
x:CompileBindings="True"
Title="File Path Character Replacement">
<Grid RowDefinitions="*,Auto">
<DataGrid
Grid.Row="0"
Grid.ColumnSpan="2"
GridLinesVisibility="All"
Margin="5"
Name="replacementGrid"
AutoGenerateColumns="False"
IsReadOnly="False"
BeginningEdit="ReplacementGrid_BeginningEdit"
CellEditEnding="ReplacementGrid_CellEditEnding"
KeyDown="ReplacementGrid_KeyDown"
ItemsSource="{CompiledBinding replacements}">
GridLinesVisibility="All"
CanUserSortColumns="False"
AutoGenerateColumns="False"
ItemsSource="{Binding Replacements}"
KeyDown="replacementGrid_KeyDown"
BeginningEdit="replacementGrid_BeginningEdit"
CellEditEnded="replacementGrid_CellEditEnded"
CellEditEnding="replacementGrid_CellEditEnding">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Char to&#xa;Replace">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate x:DataType="dialogs:EditReplacementChars+ReplacementsExt">
<TextBox IsReadOnly="{CompiledBinding Mandatory}" Text="{CompiledBinding CharacterToReplace, Mode=TwoWay}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Replacement&#xa;Text">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate x:DataType="dialogs:EditReplacementChars+ReplacementsExt">
<TextBox Text="{CompiledBinding ReplacementText, Mode=TwoWay}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<controls:DataGridTextColumnExt
x:DataType="dialogs:EditReplacementChars+ReplacementsExt"
MaxLength="1"
Header="Char to&#xa;Replace"
Binding="{Binding CharacterToReplace, Mode=TwoWay}"/>
<DataGridTemplateColumn Width="*" Header="Description">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate x:DataType="dialogs:EditReplacementChars+ReplacementsExt">
<TextBox IsReadOnly="{CompiledBinding Mandatory}" Text="{CompiledBinding Description, Mode=TwoWay}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn
x:DataType="dialogs:EditReplacementChars+ReplacementsExt"
Header="Replacement&#xa;Text"
Binding="{Binding ReplacementText, Mode=TwoWay}"/>
<DataGridTextColumn
x:DataType="dialogs:EditReplacementChars+ReplacementsExt"
Header="Description"
Binding="{Binding Description, Mode=TwoWay}"/>
</DataGrid.Columns>
</DataGrid>
<Grid
Grid.Row="1"
Grid.Column="0"
RowDefinitions="Auto,Auto"
Margin="5"
ColumnDefinitions="Auto,Auto,Auto,Auto">
ColumnDefinitions="Auto,Auto,Auto,*,Auto,Auto"
Margin="5">
<Grid.Styles>
<Style Selector="Button">
<Setter Property="Margin" Value="2"/>
<Setter Property="Padding" Value="6"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
</Style>
<Style Selector="TextBlock">
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</Grid.Styles>
<TextBlock IsVisible="{CompiledBinding !EnvironmentIsWindows}" Text="This System:" Margin="0,0,10,0" VerticalAlignment="Center" />
<TextBlock IsVisible="{CompiledBinding !EnvironmentIsWindows}" Grid.Row="1" Text="NTFS:" Margin="0,0,10,0" VerticalAlignment="Center" />
<TextBlock Grid.Row="0" Text="This&#xa;System:" IsVisible="{Binding !EnvironmentIsWindows}" />
<TextBlock Grid.Row="1" Text="NTFS:" IsVisible="{Binding !EnvironmentIsWindows}" />
<Button Grid.Column="1" Margin="0,0,10,0" Command="{CompiledBinding Defaults}" CommandParameter="{CompiledBinding EnvironmentIsWindows}" Content="Defaults" />
<Button Grid.Column="2" Margin="0,0,10,0" Command="{CompiledBinding LoFiDefaults}" CommandParameter="{CompiledBinding EnvironmentIsWindows}" Content="LoFi Defaults" />
<Button Grid.Column="3" Command="{CompiledBinding Barebones}" CommandParameter="{CompiledBinding EnvironmentIsWindows}" Content="Barebones" />
<Button Grid.Column="1" Command="{Binding Defaults}" CommandParameter="{Binding EnvironmentIsWindows}" Content="Defaults" />
<Button Grid.Column="2" Command="{Binding LoFiDefaults}" CommandParameter="{Binding EnvironmentIsWindows}" Content="LoFi Defaults" />
<Button Grid.Column="3" Command="{Binding Barebones}" CommandParameter="{Binding EnvironmentIsWindows}" Content="Barebones" />
<Button IsVisible="{CompiledBinding !EnvironmentIsWindows}" Grid.Row="1" Grid.Column="1" Margin="0,10,10,0" Command="{CompiledBinding Defaults}" CommandParameter="True" Content="Defaults" />
<Button IsVisible="{CompiledBinding !EnvironmentIsWindows}" Grid.Row="1" Grid.Column="2" Margin="0,10,10,0" Command="{CompiledBinding LoFiDefaults}" CommandParameter="True" Content="LoFi Defaults" />
<Button IsVisible="{CompiledBinding !EnvironmentIsWindows}" Grid.Row="1" Grid.Column="3" Margin="0,10,0,0" Command="{CompiledBinding Barebones}" CommandParameter="True" Content="Barebones" />
</Grid>
<StackPanel
Grid.Row="1"
Grid.Column="1"
Margin="5"
VerticalAlignment="Bottom"
Orientation="Horizontal">
<Button Margin="0,0,10,0" Command="{Binding Close}" Content="Cancel" />
<Button Padding="20,5,20,6" Command="{Binding SaveAndClose}" Content="Save" />
</StackPanel>
</Grid>
<Button Grid.Row="1" Grid.Column="1" IsVisible="{Binding !EnvironmentIsWindows}" Command="{Binding Defaults}" CommandParameter="True" Content="Defaults" />
<Button Grid.Row="1" Grid.Column="2" IsVisible="{Binding !EnvironmentIsWindows}" Command="{Binding LoFiDefaults}" CommandParameter="True" Content="LoFi Defaults" />
<Button Grid.Row="1" Grid.Column="3" IsVisible="{Binding !EnvironmentIsWindows}" Command="{Binding Barebones}" CommandParameter="True" Content="Barebones" />
<Button Grid.RowSpan="2" Grid.Column="4" Command="{Binding Close}" Content="Cancel" />
<Button Grid.RowSpan="2" Grid.Column="5" Padding="20,6" Command="{Binding SaveAndClose}" Content="Save" />
</Grid>
</Grid>
</Window>

View File

@@ -1,27 +1,27 @@
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Data;
using FileManager;
using LibationFileManager;
using ReactiveUI;
using System.Collections.Generic;
using System.Linq;
#nullable enable
namespace LibationAvalonia.Dialogs
{
public partial class EditReplacementChars : DialogWindow
{
Configuration config;
private Configuration? Config { get; }
public bool EnvironmentIsWindows => Configuration.IsWindows;
private readonly List<ReplacementsExt> SOURCE = new();
public DataGridCollectionView replacements { get; }
private readonly AvaloniaList<ReplacementsExt> SOURCE = new();
public DataGridCollectionView Replacements { get; }
public EditReplacementChars()
{
InitializeComponent();
replacements = new(SOURCE);
Replacements = new(SOURCE);
if (Design.IsDesignMode)
{
@@ -33,7 +33,7 @@ namespace LibationAvalonia.Dialogs
public EditReplacementChars(Configuration config) : this()
{
this.config = config;
Config = config;
LoadTable(config.ReplacementCharacters.Replacements);
}
@@ -44,15 +44,14 @@ namespace LibationAvalonia.Dialogs
public void Barebones(bool isNtfs)
=> LoadTable(ReplacementCharacters.Barebones(isNtfs).Replacements);
protected override void SaveAndClose()
public new void Close() => base.Close();
public new void SaveAndClose()
{
var replacements = SOURCE
.Where(r => !r.IsDefault)
.Select(r => new Replacement(r.Character, r.ReplacementText, r.Description) { Mandatory = r.Mandatory })
.ToList();
if (config is not null)
config.ReplacementCharacters = new ReplacementCharacters { Replacements = replacements };
if (Config is not null)
{
var replacements = SOURCE.Where(r => !r.IsDefault).Select(r => r.ToReplacement()).ToArray();
Config.ReplacementCharacters = new ReplacementCharacters { Replacements = replacements };
}
base.SaveAndClose();
}
@@ -61,59 +60,64 @@ namespace LibationAvalonia.Dialogs
SOURCE.Clear();
SOURCE.AddRange(replacements.Select(r => new ReplacementsExt(r)));
SOURCE.Add(new ReplacementsExt());
this.replacements.Refresh();
}
public void ReplacementGrid_KeyDown(object sender, Avalonia.Input.KeyEventArgs e)
private bool ColumnIsCharacter(DataGridColumn column)
=> column.DisplayIndex is 0;
private bool ColumnIsReplacement(DataGridColumn column)
=> column.DisplayIndex is 1;
private bool RowIsReadOnly(DataGridRow row)
=> row.DataContext is ReplacementsExt rep && rep.Mandatory;
private bool CanDeleteSelectedItem(ReplacementsExt selectedItem)
=> !selectedItem.Mandatory && (!selectedItem.IsDefault || SOURCE[^1] != selectedItem);
private void replacementGrid_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
{
if (e.Key == Avalonia.Input.Key.Delete
&& ((DataGrid)sender).SelectedItem is ReplacementsExt repl
&& !repl.Mandatory
&& !repl.IsDefault)
{
replacements.Remove(repl);
}
e.Cancel = RowIsReadOnly(e.Row) && !ColumnIsReplacement(e.Column);
}
public void ReplacementGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
private void replacementGrid_CellEditEnding(object? sender, DataGridCellEditEndingEventArgs e)
{
var replacement = e.Row.DataContext as ReplacementsExt;
var colBinding = columnBindingPath(e.Column);
//Prevent duplicate CharacterToReplace
if (e.EditingElement is TextBox tbox
&& colBinding == nameof(replacement.CharacterToReplace)
&& SOURCE.Any(r => r != replacement && r.CharacterToReplace == tbox.Text))
//Disallow duplicates of CharacterToReplace
if (ColumnIsCharacter(e.Column) && e.Row.DataContext is ReplacementsExt r && r.CharacterToReplace.Length > 0 && SOURCE.Count(rep => rep.CharacterToReplace == r.CharacterToReplace) > 1)
{
tbox.Text = replacement.CharacterToReplace;
}
//Add new blank row
void Replacement_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (!SOURCE.Any(r => r.IsDefault))
{
var rewRepl = new ReplacementsExt();
SOURCE.Add(rewRepl);
}
replacement.PropertyChanged -= Replacement_PropertyChanged;
}
replacement.PropertyChanged += Replacement_PropertyChanged;
}
public void ReplacementGrid_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
{
var replacement = e.Row.DataContext as ReplacementsExt;
//Disallow editing of Mandatory CharacterToReplace and Descriptions
if (replacement.Mandatory
&& columnBindingPath(e.Column) != nameof(replacement.ReplacementText))
r.CharacterToReplace = "";
e.Cancel = true;
}
}
private static string columnBindingPath(DataGridColumn column)
=> ((Binding)((DataGridBoundColumn)column).Binding).Path;
private void replacementGrid_CellEditEnded(object? sender, DataGridCellEditEndedEventArgs e)
{
if (ColumnIsCharacter(e.Column) && e.Row.DataContext is ReplacementsExt r && r.CharacterToReplace.Length > 0 && !SOURCE[^1].IsDefault)
{
Replacements.AddNew();
}
}
private void replacementGrid_KeyDown(object? sender, Avalonia.Input.KeyEventArgs e)
{
if (e.Key == Avalonia.Input.Key.Delete && (sender as DataGrid)?.SelectedItem is ReplacementsExt r && CanDeleteSelectedItem(r))
{
if (Replacements.IsEditingItem)
{
if (Replacements.CanCancelEdit)
Replacements.CancelEdit();
else
Replacements.CommitEdit();
}
if (Replacements.IsAddingNew)
{
Replacements.CancelNew();
}
if (Replacements.CanRemove)
{
Replacements.Remove(r);
}
}
}
public class ReplacementsExt : ViewModels.ViewModelBase
{
@@ -122,7 +126,6 @@ namespace LibationAvalonia.Dialogs
_replacementText = string.Empty;
_description = string.Empty;
_characterToReplace = string.Empty;
IsDefault = true;
}
public ReplacementsExt(Replacement replacement)
{
@@ -131,41 +134,19 @@ namespace LibationAvalonia.Dialogs
_description = replacement.Description;
Mandatory = replacement.Mandatory;
}
private string _replacementText;
private string _description;
private string _characterToReplace;
public bool Mandatory { get; }
public string ReplacementText
{
get => _replacementText;
set
{
if (ReplacementCharacters.ContainsInvalidFilenameChar(value))
this.RaisePropertyChanged(nameof(ReplacementText));
else
this.RaiseAndSetIfChanged(ref _replacementText, value);
}
}
public string ReplacementText { get => _replacementText; set => this.RaiseAndSetIfChanged(ref _replacementText, value); }
public string Description { get => _description; set => this.RaiseAndSetIfChanged(ref _description, value); }
public string CharacterToReplace
{
get => _characterToReplace;
set
{
if (value?.Length != 1)
this.RaisePropertyChanged(nameof(CharacterToReplace));
else
{
IsDefault = false;
this.RaiseAndSetIfChanged(ref _characterToReplace, value);
}
}
}
public string CharacterToReplace { get => _characterToReplace; set => this.RaiseAndSetIfChanged(ref _characterToReplace, value); }
public char Character => string.IsNullOrEmpty(_characterToReplace) ? default : _characterToReplace[0];
public bool IsDefault { get; private set; }
public bool IsDefault => !Mandatory && string.IsNullOrEmpty(CharacterToReplace);
public bool Mandatory { get; }
public Replacement ToReplacement()
=> new(Character, ReplacementText, Description) { Mandatory = Mandatory };
}
}
}

View File

@@ -28,7 +28,7 @@ namespace LibationAvalonia.Dialogs.Login
{
try
{
if (await BrowserLoginAsync(choiceIn.LoginUrl) is ChoiceOut external)
if (Configuration.Instance.UseWebView && await BrowserLoginAsync(choiceIn.LoginUrl) is ChoiceOut external)
return external;
}
catch (Exception ex)

View File

@@ -1,15 +1,23 @@
<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="500" d:DesignHeight="200"
x:Class="LibationAvalonia.Dialogs.ScanAccountsDialog"
MinWidth="500" MinHeight="160"
Width="500" Height="200"
Title="Which Accounts?"
WindowStartupLocation="CenterOwner">
<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="500" d:DesignHeight="340"
MinWidth="200" MinHeight="210"
Width="500" Height="500"
x:Class="LibationAvalonia.Dialogs.ScanAccountsDialog"
xmlns:dialogs="clr-namespace:LibationAvalonia.Dialogs"
x:DataType="dialogs:ScanAccountsDialog"
x:CompileBindings="True"
Title="Which Accounts?"
WindowStartupLocation="CenterOwner">
<Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto,*,Auto">
<Grid
ColumnDefinitions="*,Auto"
RowDefinitions="Auto,*,Auto"
Margin="10">
<Grid.Styles>
<Style Selector="Button:focus">
@@ -22,54 +30,38 @@
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="10"
Text="Check the accounts to scan and import.&#xa;To change default selections, go to: Settings > Accounts"/>
<ScrollViewer
<DockPanel
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="10,0"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
Margin="0,10"
VerticalAlignment="Stretch">
<ListBox ItemsSource="{Binding Accounts}">
<ListBox Name="lbAccounts" ItemsSource="{Binding Accounts}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Height="20" Orientation="Horizontal">
<CheckBox
Margin="0,0,10,0"
IsChecked="{Binding IsChecked, Mode=TwoWay}" />
<TextBlock
FontSize="12"
VerticalAlignment="Center"
Text="{Binding Text}" />
</StackPanel>
<CheckBox
IsChecked="{Binding IsChecked, Mode=TwoWay}">
<TextBlock Text="{Binding Text}" />
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
<Button
Grid.Row="2"
Grid.Column="0"
Padding="20,5"
Margin="10"
Padding="20,6"
Content="Edit Accounts"
Command="{Binding EditAccountsAsync}"/>
<Button
Grid.Row="2"
Grid.Column="1"
Padding="30,5"
Margin="10"
Padding="30,6"
HorizontalAlignment="Right"
Content="Import"
Name="ImportButton"

View File

@@ -1,7 +1,6 @@
using AudibleUtilities;
using Avalonia.Controls;
using Avalonia.Collections;
using LibationUiBase.Forms;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@@ -10,41 +9,36 @@ namespace LibationAvalonia.Dialogs
{
public partial class ScanAccountsDialog : DialogWindow
{
public List<Account> CheckedAccounts { get; } = new();
private List<listItem> _accounts { get; } = new();
public IList Accounts => _accounts;
private class listItem
public IEnumerable<Account> CheckedAccounts => Accounts.Where(a => a.IsChecked).Select(a => a.Account);
public AvaloniaList<ListItem> Accounts { get; } = new();
public class ListItem
{
public Account Account { get; set; }
public string Text { get; set; }
public bool IsChecked { get; set; } = true;
public ListItem(Account account)
{
Account = account;
IsChecked = account.LibraryScan;
Text = $"{account.AccountName} ({account.AccountId} - {account.Locale.Name})";
}
public Account Account { get; }
public string Text { get; }
public bool IsChecked { get; set; }
public override string ToString() => Text;
}
public ScanAccountsDialog()
{
InitializeComponent();
ControlToFocusOnShow = this.FindControl<Button>(nameof(ImportButton));
ControlToFocusOnShow = ImportButton;
DataContext = this;
LoadAccounts();
}
private void LoadAccounts()
{
_accounts.Clear();
Accounts.Clear();
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
var accounts = persister.AccountsSettings.Accounts;
foreach (var account in accounts)
_accounts.Add(new listItem
{
Account = account,
IsChecked = account.LibraryScan,
Text = $"{account.AccountName} ({account.AccountId} - {account.Locale.Name})"
});
DataContext = this;
Accounts.AddRange(accounts.Select(account => new ListItem(account)));
}
public async Task EditAccountsAsync()
@@ -56,12 +50,7 @@ namespace LibationAvalonia.Dialogs
}
}
protected override void SaveAndClose()
{
foreach (listItem item in _accounts.Where(a => a.IsChecked))
CheckedAccounts.Add(item.Account);
base.SaveAndClose();
}
public new void SaveAndClose()
=> base.SaveAndClose();
}
}

View File

@@ -50,25 +50,25 @@
<Grid Grid.Row="1" RowDefinitions="Auto,Auto,*">
<TextBlock Text="STRING FIELDS" />
<TextBlock Grid.Row="1" Text="{CompiledBinding StringUsage}" />
<ListBox Grid.Row="2" ItemsSource="{CompiledBinding StringFields}"/>
<ListBox Grid.Row="2" DoubleTapped="ListBox_DoubleTapped" ItemsSource="{CompiledBinding StringFields}"/>
</Grid>
<Grid Grid.Row="1" Grid.Column="1" RowDefinitions="Auto,Auto,*">
<TextBlock Text="NUMBER FIELDS" />
<TextBlock Grid.Row="1" Text="{CompiledBinding NumberUsage}" />
<ListBox Grid.Row="2" ItemsSource="{CompiledBinding NumberFields}"/>
<ListBox Grid.Row="2" DoubleTapped="ListBox_DoubleTapped" ItemsSource="{CompiledBinding NumberFields}"/>
</Grid>
<Grid Grid.Row="1" Grid.Column="2" RowDefinitions="Auto,Auto,*">
<TextBlock Text="BOOLEAN (TRUE/FALSE) FIELDS" />
<TextBlock Grid.Row="1" Text="{CompiledBinding BoolUsage}" />
<ListBox Grid.Row="2" ItemsSource="{CompiledBinding BoolFields}"/>
<ListBox Grid.Row="2" DoubleTapped="ListBox_DoubleTapped" ItemsSource="{CompiledBinding BoolFields}"/>
</Grid>
<Grid Grid.Row="1" Grid.Column="3" RowDefinitions="Auto,Auto,*">
<TextBlock Text="ID FIELDS" />
<TextBlock Grid.Row="1" Text="{CompiledBinding IdUsage}" />
<ListBox Grid.Row="2" ItemsSource="{CompiledBinding IdFields}"/>
<ListBox Grid.Row="2" DoubleTapped="ListBox_DoubleTapped" ItemsSource="{CompiledBinding IdFields}"/>
</Grid>
</Grid>
</Window>

View File

@@ -1,10 +1,14 @@
using Avalonia;
using LibationSearchEngine;
using System;
using System.Linq;
#nullable enable
namespace LibationAvalonia.Dialogs
{
public partial class SearchSyntaxDialog : DialogWindow
{
public event EventHandler<string>? TagDoubleClicked;
public string StringUsage { get; }
public string NumberUsage { get; }
public string BoolUsage { get; }
@@ -51,5 +55,13 @@ namespace LibationAvalonia.Dialogs
DataContext = this;
}
private void ListBox_DoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e)
{
if (e.Source is StyledElement { DataContext: string tag })
{
TagDoubleClicked?.Invoke(this, tag);
}
}
}
}

View File

@@ -20,7 +20,7 @@ namespace LibationAvalonia.Dialogs
{
TopMessage = UpdateMessage;
Title = "Libation version 8.7.0 is now available.";
DownloadLinkText = "Libation.8.7.0-macos-chardonnay.tar.gz";
DownloadLinkText = "\r\nLibation.12.7.0-macOS-chardonnay-arm64.tgz ";
ReleaseNotes = "New features:\r\n\r\n* 'Remove' now removes forever. Removed books won't be re-added on next scan\r\n* #406 : Right Click Menu for Stop-Light Icon\r\n* #398 : Grid, right-click, copy\r\n* Add option for user to choose custom temp folder\r\n* Build Docker image\r\n\r\nEnhancements\r\n\r\n* Illegal Char Replace dialog in Chardonnay\r\n* Filename character replacement allows replacing any char, not just illegal\r\n* #352 : Better error messages for license denial\r\n* Improve 'cancel download'\r\n\r\nThanks to @Mbucari (u/MSWMan), @pixil98 (u/pixil)\r\n\r\nLibation is a free, open source audible library manager for Windows. Decrypt, backup, organize, and search your audible library\r\n\r\nI intend to keep Libation free and open source, but if you want to leave a tip, who am I to argue?";
OkText = "Yes";
DataContext = this;

View File

@@ -78,7 +78,7 @@
<PackageReference Include="Avalonia.Desktop" Version="11.3.8" />
<PackageReference Include="ReactiveUI.Avalonia" Version="11.3.8" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.8" />
<PackageReference Include="WebViewControlAvaloniaFree" Version="11.3.14" />
<PackageReference Include="WebViewControlAvaloniaFree" Version="11.3.15" />
</ItemGroup>
<ItemGroup>

View File

@@ -5,6 +5,7 @@ using System.Threading.Tasks;
using ApplicationServices;
using AppScaffolding;
using Avalonia;
using Avalonia.Controls;
using ReactiveUI.Avalonia;
using LibationFileManager;
using LibationAvalonia.Dialogs;
@@ -18,7 +19,7 @@ namespace LibationAvalonia
static class Program
{
private static System.Threading.Lock SetupLock { get; } = new();
private static bool LoggingEnabled { get; set; }
internal static bool LoggingEnabled { get; set; }
[STAThread]
static void Main(string[] args)
{
@@ -64,6 +65,13 @@ namespace LibationAvalonia
}
catch (Exception ex)
{
if (new StackTrace(ex).GetFrames().Any(f => f.GetMethod()?.DeclaringType == typeof(NativeWebDialog)))
{
//Many of the NativeWebDialog exceptions cannot be handled by user code,
//so a webview failure is a fatal error. Disable webview usage and rely
//on the external browser login method instead.
Configuration.Instance.UseWebView = false;
}
LogAndShowCrashMessage(ex);
}
}

View File

@@ -55,7 +55,7 @@ namespace LibationAvalonia.ViewModels
public void AddQuickFilterBtn() { if (SelectedNamedFilter != null) QuickFilters.Add(SelectedNamedFilter); }
public async Task FilterBtn(string filterString) => await PerformFilter(new(filterString, null));
public async Task FilterHelpBtn() => await new LibationAvalonia.Dialogs.SearchSyntaxDialog().ShowDialog(MainWindow);
public void FilterHelpBtn() => MainWindow.ShowSearchSyntaxDialog();
public void ToggleFirstFilterIsDefault() => FirstFilterIsDefault = !FirstFilterIsDefault;
public async Task EditQuickFiltersAsync() => await new LibationAvalonia.Dialogs.EditQuickFilters().ShowDialog(MainWindow);
public async Task PerformFilter(QuickFilters.NamedFilter? namedFilter)

View File

@@ -25,6 +25,7 @@ namespace LibationAvalonia.ViewModels.Settings
OverwriteExisting = config.OverwriteExisting;
CreationTime = DateTimeSources.SingleOrDefault(v => v.Value == config.CreationTime) ?? DateTimeSources[0];
LastWriteTime = DateTimeSources.SingleOrDefault(v => v.Value == config.LastWriteTime) ?? DateTimeSources[0];
UseWebView = config.UseWebView;
LoggingLevel = config.LogLevel;
GridScaleFactor = scaleFactorToLinearRange(config.GridScaleFactor);
GridFontScaleFactor = scaleFactorToLinearRange(config.GridFontScaleFactor);
@@ -41,6 +42,7 @@ namespace LibationAvalonia.ViewModels.Settings
config.OverwriteExisting = OverwriteExisting;
config.CreationTime = CreationTime.Value;
config.LastWriteTime = LastWriteTime.Value;
config.UseWebView = UseWebView;
config.LogLevel = LoggingLevel;
}
@@ -67,10 +69,11 @@ namespace LibationAvalonia.ViewModels.Settings
public List<Configuration.KnownDirectories> KnownDirectories { get; } = new()
{
Configuration.KnownDirectories.UserProfile,
Configuration.KnownDirectories.AppDir,
Configuration.KnownDirectories.MyDocs,
Configuration.KnownDirectories.LibationFiles,
Configuration.KnownDirectories.MyMusic,
Configuration.KnownDirectories.MyDocs,
Configuration.KnownDirectories.AppDir,
Configuration.KnownDirectories.UserProfile
};
public string BooksText { get; } = Configuration.GetDescription(nameof(Configuration.Books));
@@ -82,6 +85,8 @@ namespace LibationAvalonia.ViewModels.Settings
= Enum.GetValues<Configuration.DateTimeSource>()
.Select(v => new EnumDisplay<Configuration.DateTimeSource>(v))
.ToArray();
public string UseWebViewText { get; } = Configuration.GetDescription(nameof(Configuration.UseWebView));
public Serilog.Events.LogEventLevel[] LoggingLevels { get; } = Enum.GetValues<Serilog.Events.LogEventLevel>();
public string GridScaleFactorText { get; } = Configuration.GetDescription(nameof(Configuration.GridScaleFactor));
public string GridFontScaleFactorText { get; } = Configuration.GetDescription(nameof(Configuration.GridFontScaleFactor));
@@ -95,6 +100,7 @@ namespace LibationAvalonia.ViewModels.Settings
public float GridFontScaleFactor { get; set; }
public EnumDisplay<Configuration.DateTimeSource> CreationTime { get; set; }
public EnumDisplay<Configuration.DateTimeSource> LastWriteTime { get; set; }
public bool UseWebView { get; set; }
public Serilog.Events.LogEventLevel LoggingLevel { get; set; }
public string ThemeVariant

View File

@@ -223,5 +223,28 @@ namespace LibationAvalonia.Views
}
private void setProgressVisible(bool visible) => ViewModel.DownloadProgress = visible ? 0 : null;
public SearchSyntaxDialog ShowSearchSyntaxDialog()
{
var dialog = new SearchSyntaxDialog();
dialog.TagDoubleClicked += Dialog_TagDoubleClicked;
dialog.Closed += Dialog_Closed;
filterHelpBtn.IsEnabled = false;
dialog.Show(this);
return dialog;
void Dialog_Closed(object sender, EventArgs e)
{
dialog.TagDoubleClicked -= Dialog_TagDoubleClicked;
filterHelpBtn.IsEnabled = true;
}
void Dialog_TagDoubleClicked(object sender, string tag)
{
var text = filterSearchTb.Text;
filterSearchTb.Text = text.Insert(Math.Min(Math.Max(0, filterSearchTb.CaretIndex), text.Length), tag);
filterSearchTb.CaretIndex += tag.Length;
filterSearchTb.Focus();
}
}
}
}

View File

@@ -203,9 +203,10 @@ namespace LibationAvalonia
await displayControlAsync(MainForm.filterHelpBtn);
var filterHelp = new SearchSyntaxDialog();
await filterHelp.ShowDialog(MainForm);
var searchDialog = MainForm.ShowSearchSyntaxDialog();
var tcs = new TaskCompletionSource();
searchDialog.Closed += (_, _) => tcs.SetResult();
await tcs.Task;
return true;
}

View File

@@ -146,16 +146,7 @@ namespace LibationFileManager
protected override List<LongPath> GetFilePathsCustom(string productId)
{
// If user changed the BooksDirectory: reinitialize
lock (bookDirectoryFilesLocker)
{
if (BooksDirectory != BookDirectoryFiles?.RootDirectory)
{
BookDirectoryFiles?.Dispose();
BookDirectoryFiles = newBookDirectoryFiles();
}
}
ValidateBookDirectoryFiles();
var regex = GetBookSearchRegex(productId);
var diskFiles = BookDirectoryFiles?.FindFiles(regex) ?? [];
@@ -172,14 +163,25 @@ namespace LibationFileManager
public void Refresh()
{
lock (bookDirectoryFilesLocker)
{
BookDirectoryFiles ??= newBookDirectoryFiles();
}
ValidateBookDirectoryFiles();
BookDirectoryFiles?.RefreshFiles();
}
private void ValidateBookDirectoryFiles()
{
lock (bookDirectoryFilesLocker)
{
if (BooksDirectory != BookDirectoryFiles?.RootDirectory)
{
//Will happen if the user changed the Books directory
//or if BackgroundFileSystem errored out.
BookDirectoryFiles?.Dispose();
BookDirectoryFiles = newBookDirectoryFiles();
}
}
}
public LongPath? GetPath(string productId) => GetFilePath(productId);
public static async IAsyncEnumerable<FilePathCache.CacheEntry> FindAudiobooksAsync(LongPath searchDirectory, [EnumeratorCancellation] CancellationToken cancellationToken)

View File

@@ -10,8 +10,8 @@ namespace LibationFileManager
{
public partial class Configuration
{
public static string ProcessDirectory { get; } = Path.GetDirectoryName(Exe.FileLocationOnDisk)!;
public static string AppDir_Relative => $@".{Path.PathSeparator}{LIBATION_FILES_KEY}";
public static string ProcessDirectory { get; } = Path.GetDirectoryName(Environment.ProcessPath)!;
public static string AppDir_Relative => $@".{Path.DirectorySeparatorChar}{LIBATION_FILES_KEY}";
public static string AppDir_Absolute => Path.GetFullPath(Path.Combine(ProcessDirectory, LIBATION_FILES_KEY));
public static string MyDocs => Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Libation"));
public static string MyMusic => Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyMusic), "Libation"));

View File

@@ -326,6 +326,9 @@ namespace LibationFileManager
[Description("Automatically run periodic scans in the background?")]
public bool AutoScan { get => GetNonString(defaultValue: true); set => SetNonString(value); }
[Description("Use Libation's buit-in web broswer to log into Audible?")]
public bool UseWebView { get => GetNonString(defaultValue: true); set => SetNonString(value); }
[Description("Auto download books? After scan, download new books in 'checked' accounts.")]
// poorly named setting. Should just be 'AutoDownload'. It is NOT episode specific
public bool AutoDownloadEpisodes { get => GetNonString(defaultValue: false); set => SetNonString(value); }

View File

@@ -60,7 +60,7 @@ namespace LibationFileManager
#region singleton stuff
#if !DEBUG
#if DEBUG
public static Configuration CreateMockInstance()
{

View File

@@ -43,6 +43,8 @@ public class LibationContributor
GitHubUser("patienttruth"),
GitHubUser("stickystyle"),
GitHubUser("cherez"),
GitHubUser("delebash"),
GitHubUser("twsouthwick"),
]);
private LibationContributor(string name, LibationContributorType type,Uri link)

View File

@@ -20,219 +20,226 @@
base.Dispose(disposing);
}
#region Windows Form Designer generated code
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.saveBtn = new System.Windows.Forms.Button();
this.newTagsTb = new System.Windows.Forms.TextBox();
this.tagsDescLbl = new System.Windows.Forms.Label();
this.coverPb = new System.Windows.Forms.PictureBox();
this.detailsTb = new System.Windows.Forms.TextBox();
this.tagsGb = new System.Windows.Forms.GroupBox();
this.cancelBtn = new System.Windows.Forms.Button();
this.liberatedGb = new System.Windows.Forms.GroupBox();
this.pdfLiberatedCb = new System.Windows.Forms.ComboBox();
this.pdfLiberatedLbl = new System.Windows.Forms.Label();
this.bookLiberatedCb = new System.Windows.Forms.ComboBox();
this.bookLiberatedLbl = new System.Windows.Forms.Label();
this.liberatedDescLbl = new System.Windows.Forms.Label();
this.audibleLink = new System.Windows.Forms.LinkLabel();
((System.ComponentModel.ISupportInitialize)(this.coverPb)).BeginInit();
this.tagsGb.SuspendLayout();
this.liberatedGb.SuspendLayout();
this.SuspendLayout();
//
// saveBtn
//
this.saveBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.saveBtn.Location = new System.Drawing.Point(376, 427);
this.saveBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.saveBtn.Name = "saveBtn";
this.saveBtn.Size = new System.Drawing.Size(88, 27);
this.saveBtn.TabIndex = 4;
this.saveBtn.Text = "Save";
this.saveBtn.UseVisualStyleBackColor = true;
this.saveBtn.Click += new System.EventHandler(this.saveBtn_Click);
//
// newTagsTb
//
this.newTagsTb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.newTagsTb.Location = new System.Drawing.Point(7, 40);
this.newTagsTb.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.newTagsTb.Name = "newTagsTb";
this.newTagsTb.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.newTagsTb.Size = new System.Drawing.Size(556, 23);
this.newTagsTb.TabIndex = 1;
//
// tagsDescLbl
//
this.tagsDescLbl.AutoSize = true;
this.tagsDescLbl.Location = new System.Drawing.Point(7, 19);
this.tagsDescLbl.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.tagsDescLbl.Name = "tagsDescLbl";
this.tagsDescLbl.Size = new System.Drawing.Size(458, 15);
this.tagsDescLbl.TabIndex = 0;
this.tagsDescLbl.Text = "Tags are separated by a space. Each tag can contain letters, numbers, and undersc" +
"ores";
//
// coverPb
//
this.coverPb.Location = new System.Drawing.Point(12, 12);
this.coverPb.Name = "coverPb";
this.coverPb.Size = new System.Drawing.Size(80, 80);
this.coverPb.TabIndex = 3;
this.coverPb.TabStop = false;
//
// detailsTb
//
this.detailsTb.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.detailsTb.Location = new System.Drawing.Point(98, 12);
this.detailsTb.Multiline = true;
this.detailsTb.Name = "detailsTb";
this.detailsTb.ReadOnly = true;
this.detailsTb.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.detailsTb.Size = new System.Drawing.Size(484, 202);
this.detailsTb.TabIndex = 3;
//
// tagsGb
//
this.tagsGb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.tagsGb.Controls.Add(this.tagsDescLbl);
this.tagsGb.Controls.Add(this.newTagsTb);
this.tagsGb.Location = new System.Drawing.Point(12, 220);
this.tagsGb.Name = "tagsGb";
this.tagsGb.Size = new System.Drawing.Size(570, 73);
this.tagsGb.TabIndex = 0;
this.tagsGb.TabStop = false;
this.tagsGb.Text = "Edit Tags";
//
// cancelBtn
//
this.cancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.cancelBtn.Location = new System.Drawing.Point(494, 427);
this.cancelBtn.Name = "cancelBtn";
this.cancelBtn.Size = new System.Drawing.Size(88, 27);
this.cancelBtn.TabIndex = 5;
this.cancelBtn.Text = "Cancel";
this.cancelBtn.UseVisualStyleBackColor = true;
this.cancelBtn.Click += new System.EventHandler(this.cancelBtn_Click);
//
// liberatedGb
//
this.liberatedGb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.liberatedGb.Controls.Add(this.pdfLiberatedCb);
this.liberatedGb.Controls.Add(this.pdfLiberatedLbl);
this.liberatedGb.Controls.Add(this.bookLiberatedCb);
this.liberatedGb.Controls.Add(this.bookLiberatedLbl);
this.liberatedGb.Controls.Add(this.liberatedDescLbl);
this.liberatedGb.Location = new System.Drawing.Point(12, 299);
this.liberatedGb.Name = "liberatedGb";
this.liberatedGb.Size = new System.Drawing.Size(570, 122);
this.liberatedGb.TabIndex = 1;
this.liberatedGb.TabStop = false;
this.liberatedGb.Text = "Liberated status: Whether the book/pdf has been downloaded";
//
// pdfLiberatedCb
//
this.pdfLiberatedCb.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.pdfLiberatedCb.FormattingEnabled = true;
this.pdfLiberatedCb.Location = new System.Drawing.Point(244, 86);
this.pdfLiberatedCb.Name = "pdfLiberatedCb";
this.pdfLiberatedCb.Size = new System.Drawing.Size(121, 23);
this.pdfLiberatedCb.TabIndex = 4;
//
// pdfLiberatedLbl
//
this.pdfLiberatedLbl.AutoSize = true;
this.pdfLiberatedLbl.Location = new System.Drawing.Point(210, 89);
this.pdfLiberatedLbl.Name = "pdfLiberatedLbl";
this.pdfLiberatedLbl.Size = new System.Drawing.Size(28, 15);
this.pdfLiberatedLbl.TabIndex = 3;
this.pdfLiberatedLbl.Text = "PDF";
//
// bookLiberatedCb
//
this.bookLiberatedCb.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.bookLiberatedCb.FormattingEnabled = true;
this.bookLiberatedCb.Location = new System.Drawing.Point(47, 86);
this.bookLiberatedCb.Name = "bookLiberatedCb";
this.bookLiberatedCb.Size = new System.Drawing.Size(121, 23);
this.bookLiberatedCb.TabIndex = 2;
//
// bookLiberatedLbl
//
this.bookLiberatedLbl.AutoSize = true;
this.bookLiberatedLbl.Location = new System.Drawing.Point(7, 89);
this.bookLiberatedLbl.Name = "bookLiberatedLbl";
this.bookLiberatedLbl.Size = new System.Drawing.Size(34, 15);
this.bookLiberatedLbl.TabIndex = 1;
this.bookLiberatedLbl.Text = "Book";
//
// liberatedDescLbl
//
this.liberatedDescLbl.AutoSize = true;
this.liberatedDescLbl.Location = new System.Drawing.Point(20, 31);
this.liberatedDescLbl.Name = "liberatedDescLbl";
this.liberatedDescLbl.Size = new System.Drawing.Size(312, 30);
this.liberatedDescLbl.TabIndex = 0;
this.liberatedDescLbl.Text = "To download again next time: change to Not Downloaded\r\nTo not download: change to" +
" Downloaded";
//
// audibleLink
//
this.audibleLink.AutoSize = true;
this.audibleLink.Location = new System.Drawing.Point(12, 169);
this.audibleLink.Name = "audibleLink";
this.audibleLink.Size = new System.Drawing.Size(57, 45);
this.audibleLink.TabIndex = 2;
this.audibleLink.TabStop = true;
this.audibleLink.Text = "Open in\r\nAudible\r\n(browser)";
this.audibleLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.audibleLink_LinkClicked);
//
// BookDetailsDialog
//
this.AcceptButton = this.saveBtn;
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.CancelButton = this.cancelBtn;
this.ClientSize = new System.Drawing.Size(594, 466);
this.Controls.Add(this.audibleLink);
this.Controls.Add(this.liberatedGb);
this.Controls.Add(this.cancelBtn);
this.Controls.Add(this.tagsGb);
this.Controls.Add(this.detailsTb);
this.Controls.Add(this.coverPb);
this.Controls.Add(this.saveBtn);
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 = "BookDetailsDialog";
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Book Details";
((System.ComponentModel.ISupportInitialize)(this.coverPb)).EndInit();
this.tagsGb.ResumeLayout(false);
this.tagsGb.PerformLayout();
this.liberatedGb.ResumeLayout(false);
this.liberatedGb.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
saveBtn = new System.Windows.Forms.Button();
newTagsTb = new System.Windows.Forms.TextBox();
tagsDescLbl = new System.Windows.Forms.Label();
coverPb = new System.Windows.Forms.PictureBox();
detailsTb = new System.Windows.Forms.TextBox();
tagsGb = new System.Windows.Forms.GroupBox();
cancelBtn = new System.Windows.Forms.Button();
liberatedGb = new System.Windows.Forms.GroupBox();
pdfLiberatedCb = new System.Windows.Forms.ComboBox();
pdfLiberatedLbl = new System.Windows.Forms.Label();
bookLiberatedCb = new System.Windows.Forms.ComboBox();
bookLiberatedLbl = new System.Windows.Forms.Label();
liberatedDescLbl = new System.Windows.Forms.Label();
audibleLink = new System.Windows.Forms.LinkLabel();
dolbyAtmosPb = new System.Windows.Forms.PictureBox();
((System.ComponentModel.ISupportInitialize)coverPb).BeginInit();
tagsGb.SuspendLayout();
liberatedGb.SuspendLayout();
((System.ComponentModel.ISupportInitialize)dolbyAtmosPb).BeginInit();
SuspendLayout();
//
// saveBtn
//
saveBtn.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right;
saveBtn.Location = new System.Drawing.Point(376, 427);
saveBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
saveBtn.Name = "saveBtn";
saveBtn.Size = new System.Drawing.Size(88, 27);
saveBtn.TabIndex = 4;
saveBtn.Text = "Save";
saveBtn.UseVisualStyleBackColor = true;
saveBtn.Click += saveBtn_Click;
//
// newTagsTb
//
newTagsTb.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
newTagsTb.Location = new System.Drawing.Point(7, 40);
newTagsTb.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
newTagsTb.Name = "newTagsTb";
newTagsTb.ScrollBars = System.Windows.Forms.ScrollBars.Both;
newTagsTb.Size = new System.Drawing.Size(556, 23);
newTagsTb.TabIndex = 1;
//
// tagsDescLbl
//
tagsDescLbl.AutoSize = true;
tagsDescLbl.Location = new System.Drawing.Point(7, 19);
tagsDescLbl.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
tagsDescLbl.Name = "tagsDescLbl";
tagsDescLbl.Size = new System.Drawing.Size(459, 15);
tagsDescLbl.TabIndex = 0;
tagsDescLbl.Text = "Tags are separated by a space. Each tag can contain letters, numbers, and underscores";
//
// coverPb
//
coverPb.Location = new System.Drawing.Point(12, 12);
coverPb.Name = "coverPb";
coverPb.Size = new System.Drawing.Size(80, 80);
coverPb.TabIndex = 3;
coverPb.TabStop = false;
//
// detailsTb
//
detailsTb.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
detailsTb.Location = new System.Drawing.Point(98, 12);
detailsTb.Multiline = true;
detailsTb.Name = "detailsTb";
detailsTb.ReadOnly = true;
detailsTb.ScrollBars = System.Windows.Forms.ScrollBars.Both;
detailsTb.Size = new System.Drawing.Size(484, 202);
detailsTb.TabIndex = 3;
//
// tagsGb
//
tagsGb.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
tagsGb.Controls.Add(tagsDescLbl);
tagsGb.Controls.Add(newTagsTb);
tagsGb.Location = new System.Drawing.Point(12, 220);
tagsGb.Name = "tagsGb";
tagsGb.Size = new System.Drawing.Size(570, 73);
tagsGb.TabIndex = 0;
tagsGb.TabStop = false;
tagsGb.Text = "Edit Tags";
//
// cancelBtn
//
cancelBtn.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right;
cancelBtn.Location = new System.Drawing.Point(494, 427);
cancelBtn.Name = "cancelBtn";
cancelBtn.Size = new System.Drawing.Size(88, 27);
cancelBtn.TabIndex = 5;
cancelBtn.Text = "Cancel";
cancelBtn.UseVisualStyleBackColor = true;
cancelBtn.Click += cancelBtn_Click;
//
// liberatedGb
//
liberatedGb.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
liberatedGb.Controls.Add(pdfLiberatedCb);
liberatedGb.Controls.Add(pdfLiberatedLbl);
liberatedGb.Controls.Add(bookLiberatedCb);
liberatedGb.Controls.Add(bookLiberatedLbl);
liberatedGb.Controls.Add(liberatedDescLbl);
liberatedGb.Location = new System.Drawing.Point(12, 299);
liberatedGb.Name = "liberatedGb";
liberatedGb.Size = new System.Drawing.Size(570, 122);
liberatedGb.TabIndex = 1;
liberatedGb.TabStop = false;
liberatedGb.Text = "Liberated status: Whether the book/pdf has been downloaded";
//
// pdfLiberatedCb
//
pdfLiberatedCb.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
pdfLiberatedCb.FormattingEnabled = true;
pdfLiberatedCb.Location = new System.Drawing.Point(244, 86);
pdfLiberatedCb.Name = "pdfLiberatedCb";
pdfLiberatedCb.Size = new System.Drawing.Size(121, 23);
pdfLiberatedCb.TabIndex = 4;
//
// pdfLiberatedLbl
//
pdfLiberatedLbl.AutoSize = true;
pdfLiberatedLbl.Location = new System.Drawing.Point(210, 89);
pdfLiberatedLbl.Name = "pdfLiberatedLbl";
pdfLiberatedLbl.Size = new System.Drawing.Size(28, 15);
pdfLiberatedLbl.TabIndex = 3;
pdfLiberatedLbl.Text = "PDF";
//
// bookLiberatedCb
//
bookLiberatedCb.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
bookLiberatedCb.FormattingEnabled = true;
bookLiberatedCb.Location = new System.Drawing.Point(47, 86);
bookLiberatedCb.Name = "bookLiberatedCb";
bookLiberatedCb.Size = new System.Drawing.Size(121, 23);
bookLiberatedCb.TabIndex = 2;
//
// bookLiberatedLbl
//
bookLiberatedLbl.AutoSize = true;
bookLiberatedLbl.Location = new System.Drawing.Point(7, 89);
bookLiberatedLbl.Name = "bookLiberatedLbl";
bookLiberatedLbl.Size = new System.Drawing.Size(34, 15);
bookLiberatedLbl.TabIndex = 1;
bookLiberatedLbl.Text = "Book";
//
// liberatedDescLbl
//
liberatedDescLbl.AutoSize = true;
liberatedDescLbl.Location = new System.Drawing.Point(20, 31);
liberatedDescLbl.Name = "liberatedDescLbl";
liberatedDescLbl.Size = new System.Drawing.Size(312, 30);
liberatedDescLbl.TabIndex = 0;
liberatedDescLbl.Text = "To download again next time: change to Not Downloaded\r\nTo not download: change to Downloaded";
//
// audibleLink
//
audibleLink.Location = new System.Drawing.Point(12, 169);
audibleLink.Name = "audibleLink";
audibleLink.Size = new System.Drawing.Size(80, 45);
audibleLink.TabIndex = 2;
audibleLink.TabStop = true;
audibleLink.Text = "Open in\r\nAudible\r\n(browser)";
audibleLink.TextAlign = System.Drawing.ContentAlignment.TopCenter;
audibleLink.LinkClicked += audibleLink_LinkClicked;
//
// dolbyAtmosPb
//
dolbyAtmosPb.Image = Properties.Resources.Dolby_Atmos_Vertical_80;
dolbyAtmosPb.Location = new System.Drawing.Point(12, 112);
dolbyAtmosPb.Name = "dolbyAtmosPb";
dolbyAtmosPb.Size = new System.Drawing.Size(80, 36);
dolbyAtmosPb.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage;
dolbyAtmosPb.TabIndex = 6;
dolbyAtmosPb.TabStop = false;
//
// BookDetailsDialog
//
AcceptButton = saveBtn;
AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
CancelButton = cancelBtn;
ClientSize = new System.Drawing.Size(594, 466);
Controls.Add(dolbyAtmosPb);
Controls.Add(audibleLink);
Controls.Add(liberatedGb);
Controls.Add(cancelBtn);
Controls.Add(tagsGb);
Controls.Add(detailsTb);
Controls.Add(coverPb);
Controls.Add(saveBtn);
FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
MaximizeBox = false;
MinimizeBox = false;
Name = "BookDetailsDialog";
ShowInTaskbar = false;
StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
Text = "Book Details";
((System.ComponentModel.ISupportInitialize)coverPb).EndInit();
tagsGb.ResumeLayout(false);
tagsGb.PerformLayout();
liberatedGb.ResumeLayout(false);
liberatedGb.PerformLayout();
((System.ComponentModel.ISupportInitialize)dolbyAtmosPb).EndInit();
ResumeLayout(false);
PerformLayout();
}
}
#endregion
private System.Windows.Forms.Button saveBtn;
#endregion
private System.Windows.Forms.Button saveBtn;
private System.Windows.Forms.TextBox newTagsTb;
private System.Windows.Forms.Label tagsDescLbl;
private System.Windows.Forms.PictureBox coverPb;
@@ -246,5 +253,6 @@
private System.Windows.Forms.Label bookLiberatedLbl;
private System.Windows.Forms.Label liberatedDescLbl;
private System.Windows.Forms.LinkLabel audibleLink;
}
private System.Windows.Forms.PictureBox dolbyAtmosPb;
}
}

View File

@@ -39,6 +39,7 @@ namespace LibationWinForms.Dialogs
private void initDetails()
{
this.Text = Book.TitleWithSubtitle;
dolbyAtmosPb.Visible = Book.IsSpatial;
(_, var picture) = PictureStorage.GetPicture(new PictureDefinition(Book.PictureId, PictureSize._80x80));
this.coverPb.Image = WinFormsUtil.TryLoadImageOrDefault(picture, PictureSize._80x80);

View File

@@ -100,6 +100,16 @@ namespace LibationWinForms.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap Dolby_Atmos_Vertical_80 {
get {
object obj = ResourceManager.GetObject("Dolby_Atmos_Vertical_80", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>

View File

@@ -121,64 +121,67 @@
<data name="cheers" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\cheers.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="default_cover_300x300" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\img-coverart-prod-unavailable_300x300.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
<data name="liberate_red_pdf_yes" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\liberate_red_pdf_yes.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="default_cover_500x500" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\img-coverart-prod-unavailable_500x500.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="default_cover_80x80" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\img-coverart-prod-unavailable_80x80.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="edit_25x25" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\edit_25x25.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
<data name="liberate_yellow" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\liberate_yellow.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="edit_64x64" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\edit_64x64.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="edit_tags_25x25" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\edit-tags-25x25.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="edit_tags_50x50" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\edit-tags-50x50.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="error" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\error.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="import_16x16" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\import_16x16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
<data name="default_cover_300x300" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\img-coverart-prod-unavailable_300x300.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="liberate_green" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\liberate_green.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="liberate_green_pdf_no" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\liberate_green_pdf_no.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
<data name="liberate_red" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\liberate_red.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="edit_tags_50x50" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\edit-tags-50x50.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="liberate_green_pdf_yes" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\liberate_green_pdf_yes.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="liberate_red" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\liberate_red.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="liberate_red_pdf_no" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\liberate_red_pdf_no.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="liberate_red_pdf_yes" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\liberate_red_pdf_yes.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="liberate_yellow" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\liberate_yellow.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="liberate_yellow_pdf_no" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\liberate_yellow_pdf_no.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="liberate_yellow_pdf_yes" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\liberate_yellow_pdf_yes.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="minus" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\minus.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
<data name="edit_25x25" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\edit_25x25.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="plus" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\plus.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="minus" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\minus.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="error" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\error.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="liberate_yellow_pdf_yes" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\liberate_yellow_pdf_yes.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="liberate_red_pdf_no" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\liberate_red_pdf_no.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="default_cover_80x80" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\img-coverart-prod-unavailable_80x80.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="import_16x16" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\import_16x16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="liberate_green_pdf_no" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\liberate_green_pdf_no.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="liberate_yellow_pdf_no" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\liberate_yellow_pdf_no.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="edit_tags_25x25" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\edit-tags-25x25.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Dolby_Atmos_Vertical_80" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Dolby_Atmos_Vertical_80.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB