Merge pull request #1444 from Mbucari/master

Use C# 14 `field` and extension members.
This commit is contained in:
rmcrackan
2025-11-23 22:44:57 -05:00
committed by GitHub
57 changed files with 404 additions and 435 deletions

View File

@@ -1,6 +1,6 @@
using FileManager;
using System.Threading.Tasks;
#nullable enable
namespace AaxDecrypter
{
public class UnencryptedAudiobookDownloader : AudiobookDownloadBase

View File

@@ -243,6 +243,7 @@ namespace AppScaffolding
//Log.Logger.Here().Debug("Begin Libation. Debug with line numbers");
}
#nullable enable
private static void logStartupState(Configuration config)
{
#if DEBUG
@@ -256,9 +257,11 @@ namespace AppScaffolding
// begin logging session with a form feed
Log.Logger.Information("\r\n\f");
static int fileCount(FileManager.LongPath longPath)
static int fileCount(FileManager.LongPath? longPath)
{
try { return FileManager.FileUtility.SaferEnumerateFiles(longPath).Count(); }
if (longPath is null)
return -1;
try { return FileManager.FileUtility.SaferEnumerateFiles(longPath).Count(); }
catch { return -1; }
}
@@ -298,8 +301,8 @@ namespace AppScaffolding
if (InteropFactory.InteropFunctionsType is null)
Serilog.Log.Logger.Warning("WARNING: OSInteropProxy.InteropFunctionsType is null");
}
private static void wireUpSystemEvents(Configuration configuration)
#nullable restore
private static void wireUpSystemEvents(Configuration configuration)
{
LibraryCommands.LibrarySizeChanged += (object _, List<DataLayer.LibraryBook> libraryBooks)
=> SearchEngineCommands.FullReIndex(libraryBooks);

View File

@@ -577,7 +577,7 @@ namespace ApplicationServices
// must be here instead of in db layer due to AaxcExists
public static LiberatedStatus Liberated_Status(Book book)
=> book.Audio_Exists() ? book.UserDefinedItem.BookStatus
=> book.AudioExists ? book.UserDefinedItem.BookStatus
: AudibleFileStorage.AaxcExists(book.AudibleProductId) ? LiberatedStatus.PartialDownload
: LiberatedStatus.NotLiberated;
@@ -645,7 +645,7 @@ namespace ApplicationServices
var pdfResults = libraryBooks
.AsParallel()
.Where(lb => lb.Book.HasPdf())
.Where(lb => lb.Book.HasPdf)
.Select(lb => new { absent = lb.AbsentFromLastScan, status = Pdf_Status(lb.Book) })
.ToList();

View File

@@ -150,12 +150,12 @@ namespace ApplicationServices
Locale = a.Book.Locale,
Title = a.Book.Title,
Subtitle = a.Book.Subtitle,
AuthorNames = a.Book.AuthorNames(),
NarratorNames = a.Book.NarratorNames(),
AuthorNames = a.Book.AuthorNames,
NarratorNames = a.Book.NarratorNames,
LengthInMinutes = a.Book.LengthInMinutes,
Description = a.Book.Description,
Publisher = a.Book.Publisher,
HasPdf = a.Book.HasPdf(),
HasPdf = a.Book.HasPdf,
SeriesNames = a.Book.SeriesNames(),
SeriesOrder = a.Book.SeriesLink.Any() ? a.Book.SeriesLink?.Select(sl => $"{sl.Order} : {sl.Series.Name}").Aggregate((a, b) => $"{a}, {b}") : "",
CommunityRatingOverall = a.Book.Rating?.OverallRating.ZeroIsNull(),

View File

@@ -18,68 +18,64 @@ namespace AudibleUtilities
public string AccountId { get; }
// user-friendly, non-canonical name. mutable
private string _accountName;
public string AccountName
{
get => _accountName;
get => field;
set
{
if (string.IsNullOrWhiteSpace(value))
return;
var v = value.Trim();
if (v == _accountName)
if (v == field)
return;
_accountName = v;
field = v;
update();
}
}
// whether to include this account when scanning libraries.
// technically this is an app setting; not an attribute of account. but since it's managed with accounts, it makes sense to put this exception-to-the-rule here
private bool _libraryScan = true;
public bool LibraryScan
{
get => _libraryScan;
get => field;
set
{
if (value == _libraryScan)
if (value == field)
return;
_libraryScan = value;
field = value;
update();
}
}
private string _decryptKey = "";
/// <summary>aka: activation bytes</summary>
public string DecryptKey
{
get => _decryptKey;
get => field ?? "";
set
{
var v = (value ?? "").Trim();
if (v == _decryptKey)
if (v == field)
return;
_decryptKey = v;
field = v;
update();
}
}
private Identity _identity;
public Identity IdentityTokens
{
get => _identity;
get => field;
set
{
if (_identity is null && value is null)
if (field is null && value is null)
return;
if (_identity is not null)
_identity.Updated -= update;
if (field is not null)
field.Updated -= update;
if (value is not null)
value.Updated += update;
_identity = value;
field = value;
update();
}
}

View File

@@ -5,7 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AudibleApi" Version="10.0.0.1" />
<PackageReference Include="AudibleApi" Version="10.1.0.1" />
<PackageReference Include="Google.Protobuf" Version="3.33.1" />
</ItemGroup>

View File

@@ -12,7 +12,7 @@
<ItemGroup>
<PackageReference Include="Dinah.Core" Version="10.0.0.1" />
<PackageReference Include="Dinah.EntityFrameworkCore" Version="10.0.0.1" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0-rtm-ci.20251120T065334" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.0">
<PrivateAssets>all</PrivateAssets>

View File

@@ -13,69 +13,74 @@ namespace DataLayer
.Where(a => a.Role == role)
.OrderBy(a => a.Order);
public static string TitleSortable(this Book book) => Formatters.GetSortName(book.Title + book.Subtitle);
public static string AuthorNames(this Book book) => string.Join(", ", book.Authors.Select(a => a.Name));
public static string NarratorNames(this Book book) => string.Join(", ", book.Narrators.Select(n => n.Name));
extension(Book book)
{
public string SeriesSortable() => Formatters.GetSortName(book.SeriesNames(true));
public string TitleSortable() => Formatters.GetSortName(book.Title + book.Subtitle);
/// <summary>True if IsLiberated or Error. False if NotLiberated</summary>
public static bool Audio_Exists(this Book book) => book.UserDefinedItem.BookStatus != LiberatedStatus.NotLiberated;
/// <summary>True if exists and IsLiberated. Else false</summary>
public static bool PDF_Exists(this Book book) => book.UserDefinedItem.PdfStatus == LiberatedStatus.Liberated;
public string AuthorNames => string.Join(", ", book.Authors.Select(a => a.Name));
public string NarratorNames => string.Join(", ", book.Narrators.Select(n => n.Name));
/// <summary>True if IsLiberated or Error. False if NotLiberated</summary>
public bool AudioExists => book.UserDefinedItem.BookStatus is LiberatedStatus.Liberated or LiberatedStatus.Error;
/// <summary>True if exists and IsLiberated. Else false</summary>
public bool PdfExists => book.UserDefinedItem.PdfStatus == LiberatedStatus.NotLiberated;
/// <summary> Whether the book has any supplements </summary>
public bool HasPdf => book.Supplements.Any();
public static string SeriesSortable(this Book book) => Formatters.GetSortName(book.SeriesNames(true));
public static bool HasPdf(this Book book) => book.Supplements.Any();
public static string SeriesNames(this Book book, bool includeIndex = false)
{
if (book.SeriesLink is null)
return "";
public string SeriesNames(bool includeIndex = false)
{
if (book.SeriesLink is null)
return "";
// first: alphabetical by name
var withNames = book.SeriesLink
.Where(s => !string.IsNullOrWhiteSpace(s.Series.Name))
.Select(getSeriesNameString)
.OrderBy(a => a)
.ToList();
// then un-named are alpha by series id
var nullNames = book.SeriesLink
.Where(s => string.IsNullOrWhiteSpace(s.Series.Name))
.Select(s => s.Series.AudibleSeriesId)
.OrderBy(a => a)
.ToList();
// first: alphabetical by name
var withNames = book.SeriesLink
.Where(s => !string.IsNullOrWhiteSpace(s.Series.Name))
.Select(getSeriesNameString)
.OrderBy(a => a)
.ToList();
// then un-named are alpha by series id
var nullNames = book.SeriesLink
.Where(s => string.IsNullOrWhiteSpace(s.Series.Name))
.Select(s => s.Series.AudibleSeriesId)
.OrderBy(a => a)
.ToList();
var all = withNames.Union(nullNames).ToList();
return string.Join(", ", all);
var all = withNames.Union(nullNames).ToList();
return string.Join(", ", all);
string getSeriesNameString(SeriesBook sb)
=> includeIndex && !string.IsNullOrWhiteSpace(sb.Order) && sb.Order != "-1"
? $"{sb.Series.Name} (#{sb.Order})"
: sb.Series.Name;
string getSeriesNameString(SeriesBook sb)
=> includeIndex && !string.IsNullOrWhiteSpace(sb.Order) && sb.Order != "-1"
? $"{sb.Series.Name} (#{sb.Order})"
: sb.Series.Name;
}
public string[] LowestCategoryNames()
=> book.CategoriesLink?.Any() is not true ? Array.Empty<string>()
: book
.CategoriesLink
.Select(cl => cl.CategoryLadder.Categories.LastOrDefault()?.Name)
.Where(c => c is not null)
.Distinct()
.ToArray();
public string[] AllCategoryNames()
=> book.CategoriesLink?.Any() is not true ? Array.Empty<string>()
: book
.CategoriesLink
.SelectMany(cl => cl.CategoryLadder.Categories)
.Select(c => c.Name)
.ToArray();
public string[] AllCategoryIds()
=> book.CategoriesLink?.Any() is not true ? null
: book
.CategoriesLink
.SelectMany(cl => cl.CategoryLadder.Categories)
.Select(c => c.AudibleCategoryId)
.ToArray();
}
public static string[] LowestCategoryNames(this Book book)
=> book.CategoriesLink?.Any() is not true ? Array.Empty<string>()
: book
.CategoriesLink
.Select(cl => cl.CategoryLadder.Categories.LastOrDefault()?.Name)
.Where(c => c is not null)
.Distinct()
.ToArray();
public static string[] AllCategoryNames(this Book book)
=> book.CategoriesLink?.Any() is not true ? Array.Empty<string>()
: book
.CategoriesLink
.SelectMany(cl => cl.CategoryLadder.Categories)
.Select(c => c.Name)
.ToArray();
public static string[] AllCategoryIds(this Book book)
=> book.CategoriesLink?.Any() is not true ? null
: book
.CategoriesLink
.SelectMany(cl => cl.CategoryLadder.Categories)
.Select(c => c.AudibleCategoryId)
.ToArray();
public static string AggregateTitles(this IEnumerable<LibraryBook> libraryBooks, int max = 5)
{
@@ -93,7 +98,7 @@ namespace DataLayer
return titlesAgg;
}
public static float FirstScore(this Rating rating)
public static float FirstScore(this Rating rating)
=> rating.OverallRating > 0 ? rating.OverallRating
: rating.PerformanceRating > 0 ? rating.PerformanceRating
: rating.StoryRating;

View File

@@ -28,7 +28,7 @@ namespace FileLiberator
/// </summary>
public DownloadOptions.LicenseInfo? LicenseInfo { get; set; }
public override bool Validate(LibraryBook libraryBook) => !libraryBook.Book.Audio_Exists();
public override bool Validate(LibraryBook libraryBook) => !libraryBook.Book.AudioExists;
public override async Task CancelAsync()
{
if (abDownloader is not null) await abDownloader.CancelAsync();
@@ -43,7 +43,7 @@ namespace FileLiberator
try
{
if (libraryBook.Book.Audio_Exists())
if (libraryBook.Book.AudioExists)
return new StatusHandler { "Cannot find decrypt. Final audio file already exists" };
DownloadValidation(libraryBook);
@@ -129,20 +129,21 @@ namespace FileLiberator
private async Task<AudiobookDecryptResult> DownloadAudiobookAsync(AudibleApi.Api api, DownloadOptions dlOptions, CancellationToken cancellationToken)
{
var outpoutDir = AudibleFileStorage.DecryptInProgressDirectory;
var cacheDir = AudibleFileStorage.DownloadsInProgressDirectory;
//Directories are validated prior to beginning download/decrypt
var outputDir = AudibleFileStorage.DecryptInProgressDirectory!;
var cacheDir = AudibleFileStorage.DownloadsInProgressDirectory!;
var result = new AudiobookDecryptResult(false, [], []);
try
{
if (dlOptions.DrmType is not DrmType.Adrm and not DrmType.Widevine)
abDownloader = new UnencryptedAudiobookDownloader(outpoutDir, cacheDir, dlOptions);
abDownloader = new UnencryptedAudiobookDownloader(outputDir, cacheDir, dlOptions);
else
{
AaxcDownloadConvertBase converter
= dlOptions.Config.SplitFilesByChapter && dlOptions.ChapterInfo.Count > 1 ?
new AaxcDownloadMultiConverter(outpoutDir, cacheDir, dlOptions) :
new AaxcDownloadSingleConverter(outpoutDir, cacheDir, dlOptions);
new AaxcDownloadMultiConverter(outputDir, cacheDir, dlOptions) :
new AaxcDownloadSingleConverter(outputDir, cacheDir, dlOptions);
if (dlOptions.Config.AllowLibationFixup)
converter.RetrievedMetadata += Converter_RetrievedMetadata;
@@ -176,7 +177,7 @@ namespace FileLiberator
void AbDownloader_TempFileCreated(object? sender, TempFile e)
{
if (Path.GetDirectoryName(e.FilePath) == outpoutDir)
if (Path.GetDirectoryName(e.FilePath) == outputDir)
{
result.ResultFiles.Add(e);
}

View File

@@ -18,7 +18,7 @@ namespace FileLiberator
public override string Name => "Download Pdf";
public override bool Validate(LibraryBook libraryBook)
=> !string.IsNullOrWhiteSpace(getdownloadUrl(libraryBook))
&& !libraryBook.Book.PDF_Exists();
&& !libraryBook.Book.PdfExists;
public override async Task<StatusHandler> ProcessAsync(LibraryBook libraryBook)
{

View File

@@ -21,9 +21,7 @@ namespace LibationAvalonia.Controls
public class CheckBoxViewModel : ViewModelBase
{
private bool _isChecked;
public bool IsChecked { get => _isChecked; set => this.RaiseAndSetIfChanged(ref _isChecked, value); }
private object _bookText;
public object Item { get => _bookText; set => this.RaiseAndSetIfChanged(ref _bookText, value); }
public bool IsChecked { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
public object Item { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
}
}

View File

@@ -140,19 +140,17 @@ namespace LibationAvalonia.Controls
private class KnownDirectoryItem : ReactiveObject
{
public Configuration.KnownDirectories KnownDirectory { get; set; }
private string? _directory;
public string? Directory { get => _directory; private set => this.RaiseAndSetIfChanged(ref _directory, value); }
public string? Directory { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
public string? Name { get; }
private string? _subDir;
public string? SubDirectory
{
get => _subDir;
get => field;
set
{
_subDir = value;
field = value;
if (Configuration.GetKnownDirectoryPath(KnownDirectory) is string dir)
{
Directory = Path.Combine(dir, _subDir ?? "");
Directory = Path.Combine(dir, field ?? "");
}
}
}

View File

@@ -64,12 +64,8 @@ namespace LibationAvalonia.Dialogs
public class AboutVM : ViewModelBase
{
public string Version { get; }
public bool CanCheckForUpgrade { get => canCheckForUpgrade; set => this.RaiseAndSetIfChanged(ref canCheckForUpgrade, value); }
public string UpgradeButtonText { get => upgradeButtonText; set => this.RaiseAndSetIfChanged(ref upgradeButtonText, value); }
private bool canCheckForUpgrade = true;
private string upgradeButtonText = "Check for Upgrade";
public bool CanCheckForUpgrade { get => field; set => this.RaiseAndSetIfChanged(ref field, value); } = true;
public string UpgradeButtonText { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }= "Check for Upgrade";
public IEnumerable<LibationContributor> PrimaryContributors => LibationContributor.PrimaryContributors;
public IEnumerable<LibationContributor> AdditionalContributors => LibationContributor.AdditionalContributors;

View File

@@ -19,15 +19,14 @@ namespace LibationAvalonia.Dialogs
public AvaloniaList<AccountDto> Accounts { get; } = new();
public class AccountDto : ViewModels.ViewModelBase
{
private string _accountId;
public IReadOnlyList<Locale> Locales => AccountsDialog.Locales;
public bool LibraryScan { get; set; } = true;
public string AccountId
{
get => _accountId;
get => field;
set
{
this.RaiseAndSetIfChanged(ref _accountId, value);
this.RaiseAndSetIfChanged(ref field, value);
this.RaisePropertyChanged(nameof(IsDefault));
}
}

View File

@@ -17,16 +17,15 @@ namespace LibationAvalonia.Dialogs
{
public partial class BookDetailsDialog : DialogWindow
{
private LibraryBook _libraryBook;
private BookDetailsDialogViewModel _viewModel;
public LibraryBook LibraryBook
{
get => _libraryBook;
get => field;
set
{
_libraryBook = value;
Title = _libraryBook.Book.TitleWithSubtitle;
DataContext = _viewModel = new BookDetailsDialogViewModel(_libraryBook);
field = value;
Title = field.Book.TitleWithSubtitle;
DataContext = _viewModel = new BookDetailsDialogViewModel(field);
}
}
@@ -116,16 +115,16 @@ namespace LibationAvalonia.Dialogs
var title = string.IsNullOrEmpty(Book.Subtitle) ? Book.Title : $"{Book.Title}\r\n {Book.Subtitle}";
//init book details
DetailsText = @$"
Title: {title}
Author(s): {Book.AuthorNames()}
Narrator(s): {Book.NarratorNames()}
Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")}
Category: {string.Join(", ", Book.LowestCategoryNames())}
Purchase Date: {libraryBook.DateAdded:d}
Language: {Book.Language}
Audible ID: {Book.AudibleProductId}
".Trim();
DetailsText = $"""
Title: {title}
Author(s): {Book.AuthorNames}
Narrator(s): {Book.NarratorNames}
Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")}
Category: {string.Join(", ", Book.LowestCategoryNames())}
Purchase Date: {libraryBook.DateAdded:d}
Language: {Book.Language}
Audible ID: {Book.AudibleProductId}
""";
var seriesNames = libraryBook.Book.SeriesNames();
if (!string.IsNullOrWhiteSpace(seriesNames))

View File

@@ -204,9 +204,8 @@ namespace LibationAvalonia.Dialogs
private class BookRecordEntry : ViewModels.ViewModelBase
{
private const string DateFormat = "yyyy-MM-dd HH\\:mm";
private bool _ischecked;
public IRecord Record { get; }
public bool IsChecked { get => _ischecked; set => this.RaiseAndSetIfChanged(ref _ischecked, value); }
public bool IsChecked { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
public string Type => Record.GetType().Name;
public string Start => formatTimeSpan(Record.Start);
public string Created => Record.Created.ToString(DateFormat);

View File

@@ -13,29 +13,25 @@ namespace LibationAvalonia.Dialogs
public class Filter : ViewModels.ViewModelBase
{
private string _name;
public string Name
{
get => _name;
set => this.RaiseAndSetIfChanged(ref _name, value);
get => field;
set => this.RaiseAndSetIfChanged(ref field, value);
}
private string _filterString;
public string FilterString
{
get => _filterString;
get => field;
set
{
IsDefault = string.IsNullOrEmpty(value);
this.RaiseAndSetIfChanged(ref _filterString, value);
this.RaiseAndSetIfChanged(ref field, value);
this.RaisePropertyChanged(nameof(IsDefault));
}
}
public bool IsDefault { get; private set; } = true;
private bool _isTop;
private bool _isBottom;
public bool IsTop { get => _isTop; set => this.RaiseAndSetIfChanged(ref _isTop, value); }
public bool IsBottom { get => _isBottom; set => this.RaiseAndSetIfChanged(ref _isBottom, value); }
public bool IsTop { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
public bool IsBottom { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
public QuickFilters.NamedFilter AsNamedFilter() => new(FilterString, Name);

View File

@@ -123,25 +123,22 @@ namespace LibationAvalonia.Dialogs
{
public ReplacementsExt()
{
_replacementText = string.Empty;
_description = string.Empty;
_characterToReplace = string.Empty;
ReplacementText = string.Empty;
Description = string.Empty;
CharacterToReplace = string.Empty;
}
public ReplacementsExt(Replacement replacement)
{
_characterToReplace = replacement.CharacterToReplace == default ? "" : replacement.CharacterToReplace.ToString();
_replacementText = replacement.ReplacementString;
_description = replacement.Description;
CharacterToReplace = replacement.CharacterToReplace == default ? "" : replacement.CharacterToReplace.ToString();
ReplacementText = replacement.ReplacementString;
Description = replacement.Description;
Mandatory = replacement.Mandatory;
}
private string _replacementText;
private string _description;
private string _characterToReplace;
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 => this.RaiseAndSetIfChanged(ref _characterToReplace, value); }
public char Character => string.IsNullOrEmpty(_characterToReplace) ? default : _characterToReplace[0];
public string ReplacementText { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
public string Description { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
public string CharacterToReplace { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
public char Character => string.IsNullOrEmpty(CharacterToReplace) ? default : CharacterToReplace[0];
public bool IsDefault => !Mandatory && string.IsNullOrEmpty(CharacterToReplace);
public bool Mandatory { get; }

View File

@@ -101,19 +101,17 @@ public partial class EditTemplateDialog : DialogWindow
=> Go.To.Url(@"ht" + "tps://github.com/rmcrackan/Libation/blob/master/Documentation/NamingTemplates.md");
// hold the work-in-progress value. not guaranteed to be valid
private string _userTemplateText;
public string UserTemplateText
{
get => _userTemplateText;
get => field;
set
{
this.RaiseAndSetIfChanged(ref _userTemplateText, value);
this.RaiseAndSetIfChanged(ref field, value);
templateTb_TextChanged();
}
}
private string _warningText;
public string WarningText { get => _warningText; set => this.RaiseAndSetIfChanged(ref _warningText, value); }
public string WarningText { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
public string Description { get; }

View File

@@ -61,15 +61,7 @@ namespace LibationAvalonia.Dialogs
public class BitmapHolder : ViewModels.ViewModelBase
{
private Bitmap _coverImage;
public Bitmap CoverImage
{
get => _coverImage;
set
{
this.RaiseAndSetIfChanged(ref _coverImage, value);
}
}
public Bitmap CoverImage { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
}
}
}

View File

@@ -108,8 +108,7 @@ namespace LibationAvalonia.Dialogs
public class LocatedAudiobooksViewModel : ViewModelBase
{
private int _foundAsins = 0;
public AvaloniaList<Tuple<string, string>> FoundFiles { get; } = new();
public int FoundAsins { get => _foundAsins; set => this.RaiseAndSetIfChanged(ref _foundAsins, value); }
public int FoundAsins { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
}
}

View File

@@ -1,6 +1,7 @@
using AudibleApi;
using AudibleUtilities;
using Avalonia.Controls;
using Avalonia.Platform;
using Avalonia.Threading;
using LibationFileManager;
using LibationUiBase.Forms;
@@ -28,7 +29,7 @@ namespace LibationAvalonia.Dialogs.Login
{
try
{
if (Configuration.Instance.UseWebView && await BrowserLoginAsync(choiceIn.LoginUrl) is ChoiceOut external)
if (Configuration.Instance.UseWebView && await BrowserLoginAsync(choiceIn) is ChoiceOut external)
return external;
}
catch (Exception ex)
@@ -42,20 +43,19 @@ namespace LibationAvalonia.Dialogs.Login
: null;
}
private async Task<ChoiceOut?> BrowserLoginAsync(string url)
private async Task<ChoiceOut?> BrowserLoginAsync(ChoiceIn shoiceIn)
{
TaskCompletionSource<ChoiceOut?> tcs = new();
NativeWebDialog dialog = new()
{
Title = "Audible Login",
CanUserResize = true,
Source = new Uri(url)
CanUserResize = true
};
dialog.EnvironmentRequested += Dialog_EnvironmentRequested;
dialog.NavigationCompleted += Dialog_NavigationCompleted;
dialog.Closing += (_, _) => tcs.TrySetResult(null);
dialog.NavigationStarted += (_, e) =>
dialog.NavigationStarted += async (_, e) =>
{
if (e.Request?.AbsolutePath.StartsWith("/ap/maplanding") is true)
{
@@ -63,6 +63,16 @@ namespace LibationAvalonia.Dialogs.Login
dialog.Close();
}
};
dialog.AdapterCreated += (s, e) =>
{
if (dialog.TryGetCookieManager() is NativeWebViewCookieManager cookieManager)
{
foreach (System.Net.Cookie c in shoiceIn.SignInCookies)
cookieManager.AddOrUpdateCookie(c);
}
//Set the source only after loading cookies
dialog.Source = new Uri(shoiceIn.LoginUrl);
};
if (!Configuration.IsLinux && App.MainWindow is TopLevel topLevel)
dialog.Show(topLevel);
@@ -70,7 +80,27 @@ namespace LibationAvalonia.Dialogs.Login
dialog.Show();
return await tcs.Task;
}
void Dialog_EnvironmentRequested(object? sender, WebViewEnvironmentRequestedEventArgs e)
{
// Private browsing & user agent setting
switch (e)
{
case WindowsWebView2EnvironmentRequestedEventArgs webView2Args:
webView2Args.IsInPrivateModeEnabled = true;
webView2Args.AdditionalBrowserArguments = "--user-agent=\"" + Resources.User_Agent + "\"";
break;
case AppleWKWebViewEnvironmentRequestedEventArgs appleArgs:
appleArgs.NonPersistentDataStore = true;
appleArgs.ApplicationNameForUserAgent = Resources.User_Agent;
break;
case GtkWebViewEnvironmentRequestedEventArgs gtkArgs:
gtkArgs.EphemeralDataManager = true;
gtkArgs.ApplicationNameForUserAgent = Resources.User_Agent;
break;
}
}
}
private async void Dialog_NavigationCompleted(object? sender, WebViewNavigationCompletedEventArgs e)
{

View File

@@ -180,16 +180,15 @@ public partial class ThemePickerDialog : DialogWindow
public required string ThemeItemName { get; init; }
public required Action<Color, string>? ColorSetter { get; set; }
private Color _themeColor;
public Color ThemeColor
{
get => _themeColor;
get => field;
set
{
var setColors = !_themeColor.Equals(value);
this.RaiseAndSetIfChanged(ref _themeColor, value);
var setColors = !field.Equals(value);
this.RaiseAndSetIfChanged(ref field, value);
if (setColors)
ColorSetter?.Invoke(_themeColor, ThemeItemName);
ColorSetter?.Invoke(field, ThemeItemName);
}
}
}

View File

@@ -35,9 +35,7 @@ namespace LibationAvalonia.Dialogs
{
public AvaloniaList<CheckBoxViewModel> DeletedBooks { get; }
public string CheckedCountText => $"Checked : {_checkedBooksCount} of {_totalBooksCount}";
private bool _controlsEnabled = true;
public bool ControlsEnabled { get => _controlsEnabled; set => this.RaiseAndSetIfChanged(ref _controlsEnabled, value); }
public bool ControlsEnabled { get => field; set => this.RaiseAndSetIfChanged(ref field, value); } = true;
private bool? everythingChecked = false;
public bool? EverythingChecked

View File

@@ -5,8 +5,7 @@ namespace LibationAvalonia.ViewModels.Dialogs
{
public class MessageBoxViewModel
{
private string _message;
public string Message { get { return _message; } set { _message = value; } }
public string Message { get => field; set => field = value; }
public string Caption { get; } = "Message Box";
private MessageBoxButtons _button;
private MessageBoxIcon _icon;

View File

@@ -5,24 +5,14 @@ namespace LibationAvalonia.ViewModels
{
public class LiberateStatusButtonViewModel : ViewModelBase
{
private bool isSeries;
private bool isError;
private bool isButtonEnabled;
private bool expanded;
private bool redVisible = true;
private bool yellowVisible;
private bool greenVisible;
private bool pdfNotDownloadedVisible;
private bool pdfDownloadedVisible;
public bool IsError { get => isError; set => this.RaiseAndSetIfChanged(ref isError, value); }
public bool IsButtonEnabled { get => isButtonEnabled; set => this.RaiseAndSetIfChanged(ref isButtonEnabled, value); }
public bool IsSeries { get => isSeries; set => this.RaiseAndSetIfChanged(ref isSeries, value); }
public bool Expanded { get => expanded; set => this.RaiseAndSetIfChanged(ref expanded, value); }
public bool RedVisible { get => redVisible; set => this.RaiseAndSetIfChanged(ref redVisible, value); }
public bool YellowVisible { get => yellowVisible; set => this.RaiseAndSetIfChanged(ref yellowVisible, value); }
public bool GreenVisible { get => greenVisible; set => this.RaiseAndSetIfChanged(ref greenVisible, value); }
public bool PdfDownloadedVisible { get => pdfDownloadedVisible; set => this.RaiseAndSetIfChanged(ref pdfDownloadedVisible, value); }
public bool PdfNotDownloadedVisible { get => pdfNotDownloadedVisible; set => this.RaiseAndSetIfChanged(ref pdfNotDownloadedVisible, value); }
public bool IsError { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
public bool IsButtonEnabled { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
public bool IsSeries { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
public bool Expanded { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
public bool RedVisible { get => field; set => this.RaiseAndSetIfChanged(ref field, value); } = true;
public bool YellowVisible { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
public bool GreenVisible { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
public bool PdfDownloadedVisible { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
public bool PdfNotDownloadedVisible { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
}
}

View File

@@ -12,7 +12,6 @@ namespace LibationAvalonia.ViewModels
partial class MainVM
{
private Task<LibraryCommands.LibraryStats>? updateCountsTask;
private LibraryCommands.LibraryStats? _libraryStats;
/// <summary> The "Begin Book and PDF Backup" menu item header text </summary>
public string BookBackupsToolStripText { get; private set; } = "Begin Book and PDF Backups: 0";
@@ -22,10 +21,10 @@ namespace LibationAvalonia.ViewModels
/// <summary> The user's library statistics </summary>
public LibraryCommands.LibraryStats? LibraryStats
{
get => _libraryStats;
get => field;
set
{
this.RaiseAndSetIfChanged(ref _libraryStats, value);
this.RaiseAndSetIfChanged(ref field, value);
BookBackupsToolStripText
= LibraryStats?.HasPendingBooks ?? false

View File

@@ -18,14 +18,11 @@ namespace LibationAvalonia.ViewModels
private string lastGoodSearch = string.Empty;
private QuickFilters.NamedFilter? lastGoodFilter => new(lastGoodSearch, null);
private QuickFilters.NamedFilter? _selectedNamedFilter = new(string.Empty, null);
private bool _firstFilterIsDefault = true;
/// <summary> Library filterting query </summary>
public QuickFilters.NamedFilter? SelectedNamedFilter { get => _selectedNamedFilter; set => this.RaiseAndSetIfChanged(ref _selectedNamedFilter, value); }
public QuickFilters.NamedFilter? SelectedNamedFilter { get => field; set => this.RaiseAndSetIfChanged(ref field, value); } = new(string.Empty, null);
public AvaloniaList<Control> QuickFilterMenuItems { get; } = new();
/// <summary> Indicates if the first quick filter is the default filter </summary>
public bool FirstFilterIsDefault { get => _firstFilterIsDefault; set => QuickFilters.UseDefault = this.RaiseAndSetIfChanged(ref _firstFilterIsDefault, value); }
public bool FirstFilterIsDefault { get => field; set => QuickFilters.UseDefault = this.RaiseAndSetIfChanged(ref field, value); }
private void Configure_Filters()
{

View File

@@ -14,30 +14,25 @@ namespace LibationAvalonia.ViewModels
{
public partial class MainVM
{
private bool _autoScanChecked = Configuration.Instance.AutoScan;
private string _removeBooksButtonText = "Remove # Books from Libation";
private bool _removeBooksButtonEnabled = Design.IsDesignMode;
private bool _removeButtonsVisible = Design.IsDesignMode;
private int _numAccountsScanning = 2;
private int _accountsCount = 0;
public string LocateAudiobooksTip => Configuration.GetHelpText("LocateAudiobooks");
/// <summary> Auto scanning accounts is enables </summary>
public bool AutoScanChecked { get => _autoScanChecked; set => Configuration.Instance.AutoScan = this.RaiseAndSetIfChanged(ref _autoScanChecked, value); }
public bool AutoScanChecked { get => field; set => Configuration.Instance.AutoScan = this.RaiseAndSetIfChanged(ref field, value); } = Configuration.Instance.AutoScan;
/// <summary> Display text for the "Remove # Books from Libation" button </summary>
public string RemoveBooksButtonText { get => _removeBooksButtonText; set => this.RaiseAndSetIfChanged(ref _removeBooksButtonText, value); }
public string RemoveBooksButtonText { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }= "Remove # Books from Libation";
/// <summary> Indicates if the "Remove # Books from Libation" button is enabled </summary>
public bool RemoveBooksButtonEnabled { get => _removeBooksButtonEnabled; set { this.RaiseAndSetIfChanged(ref _removeBooksButtonEnabled, value); } }
public bool RemoveBooksButtonEnabled { get => field; set { this.RaiseAndSetIfChanged(ref field, value); } } = Design.IsDesignMode;
/// <summary> Indicates if the "Remove # Books from Libation" and "Done Removing" buttons should be visible </summary>
public bool RemoveButtonsVisible
{
get => _removeButtonsVisible;
get => field;
set
{
this.RaiseAndSetIfChanged(ref _removeButtonsVisible, value);
this.RaiseAndSetIfChanged(ref field, value);
this.RaisePropertyChanged(nameof(RemoveMenuItemsEnabled));
}
}
} = Design.IsDesignMode;
/// <summary> Indicates if Libation is currently scanning account(s) </summary>
public bool ActivelyScanning => _numAccountsScanning > 0;
/// <summary> Indicates if the "Remove Books" menu items are enabled</summary>
@@ -53,10 +48,10 @@ namespace LibationAvalonia.ViewModels
/// <summary> The number of accounts added to Libation </summary>
public int AccountsCount
{
get => _accountsCount;
get => field;
set
{
this.RaiseAndSetIfChanged(ref _accountsCount, value);
this.RaiseAndSetIfChanged(ref field, value);
this.RaisePropertyChanged(nameof(AnyAccounts));
this.RaisePropertyChanged(nameof(OneAccount));
this.RaisePropertyChanged(nameof(MultipleAccounts));

View File

@@ -12,15 +12,13 @@ namespace LibationAvalonia.ViewModels
{
partial class MainVM
{
private bool _queueOpen = false;
/// <summary> The Process Queue panel is open </summary>
public bool QueueOpen
{
get => _queueOpen;
get => field;
set
{
this.RaiseAndSetIfChanged(ref _queueOpen, value);
this.RaiseAndSetIfChanged(ref field, value);
QueueButtonAngle = value ? 180 : 0;
this.RaisePropertyChanged(nameof(QueueButtonAngle));
}
@@ -40,7 +38,7 @@ namespace LibationAvalonia.ViewModels
{
if (ProcessQueue.QueueDownloadDecrypt(libraryBooks))
setQueueCollapseState(false);
else if (libraryBooks.Length == 1 && libraryBooks[0].Book.Audio_Exists())
else if (libraryBooks.Length == 1 && libraryBooks[0].Book.AudioExists)
{
// liberated: open explorer to file
var filePath = AudibleFileStorage.Audio.GetPath(libraryBooks[0].Book.AudibleProductId);

View File

@@ -9,8 +9,7 @@ namespace LibationAvalonia.ViewModels
{
partial class MainVM
{
private bool _menuBarVisible = !Configuration.IsMacOs;
public bool MenuBarVisible { get => _menuBarVisible; set => this.RaiseAndSetIfChanged(ref _menuBarVisible, value); }
public bool MenuBarVisible { get => field; set => this.RaiseAndSetIfChanged(ref field, value); } = !Configuration.IsMacOs;
private void Configure_Settings()
{
if (App.Current is Avalonia.Application app &&

View File

@@ -16,8 +16,7 @@ namespace LibationAvalonia.ViewModels
public ProcessQueueViewModel ProcessQueue { get; } = new ProcessQueueViewModel();
public ProductsDisplayViewModel ProductsDisplay { get; } = new ProductsDisplayViewModel();
private double? _downloadProgress = null;
public double? DownloadProgress { get => _downloadProgress; set => this.RaiseAndSetIfChanged(ref _downloadProgress, value); }
public double? DownloadProgress { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
private readonly MainWindow MainWindow;

View File

@@ -31,15 +31,8 @@ namespace LibationAvalonia.ViewModels
private HashSet<GridEntry>? FilteredInGridEntries;
public string? FilterString { get; private set; }
private DataGridCollectionView? _gridEntries;
public DataGridCollectionView? GridEntries
{
get => _gridEntries;
private set => this.RaiseAndSetIfChanged(ref _gridEntries, value);
}
private bool _removeColumnVisible;
public bool RemoveColumnVisible { get => _removeColumnVisible; private set => this.RaiseAndSetIfChanged(ref _removeColumnVisible, value); }
public DataGridCollectionView? GridEntries { get => field; private set => this.RaiseAndSetIfChanged(ref field, value); }
public bool RemoveColumnVisible { get => field; private set => this.RaiseAndSetIfChanged(ref field, value); }
public List<LibraryBook> GetVisibleBookEntries()
=> FilteredInGridEntries?
@@ -104,8 +97,7 @@ namespace LibationAvalonia.ViewModels
internal async Task BindToGridAsync(List<LibraryBook> dbBooks)
{
if (dbBooks == null)
throw new ArgumentNullException(nameof(dbBooks));
ArgumentNullException.ThrowIfNull(dbBooks, nameof(dbBooks));
//Get the UI thread's synchronization context and set it on the current thread to ensure
//it's available for GetAllProductsAsync and GetAllSeriesEntriesAsync
@@ -158,8 +150,7 @@ namespace LibationAvalonia.ViewModels
/// </summary>
internal async Task UpdateGridAsync(List<LibraryBook> dbBooks)
{
if (dbBooks == null)
throw new ArgumentNullException(nameof(dbBooks));
ArgumentNullException.ThrowIfNull(dbBooks, nameof(dbBooks));
if (GridEntries == null)
throw new InvalidOperationException($"Must call {nameof(BindToGridAsync)} before calling {nameof(UpdateGridAsync)}");

View File

@@ -13,14 +13,6 @@ namespace LibationAvalonia.ViewModels.Settings
{
public class AudioSettingsVM : ViewModelBase
{
private bool _downloadClipsBookmarks;
private bool _decryptToLossy;
private bool _splitFilesByChapter;
private bool _allowLibationFixup;
private bool _lameTargetBitrate;
private bool _lameMatchSource;
private int _lameBitrate;
private int _lameVBRQuality;
private string _chapterTitleTemplate;
public EnumDisplay<SampleRate> SelectedSampleRate { get; set; }
public NAudio.Lame.EncoderQuality SelectedEncoderQuality { get; set; }
@@ -31,13 +23,11 @@ namespace LibationAvalonia.ViewModels.Settings
.Select(v => new EnumDisplay<SampleRate>(v, $"{((int)v):N0} Hz")));
public AvaloniaList<NAudio.Lame.EncoderQuality> EncoderQualities { get; }
= new(
new[]
{
= new([
NAudio.Lame.EncoderQuality.High,
NAudio.Lame.EncoderQuality.Standard,
NAudio.Lame.EncoderQuality.Fast,
});
]);
public AudioSettingsVM(Configuration config)
@@ -141,12 +131,10 @@ namespace LibationAvalonia.ViewModels.Settings
public bool DownloadCoverArt { get; set; }
public bool RetainAaxFile { get; set; }
public string RetainAaxFileTip => Configuration.GetHelpText(nameof(RetainAaxFile));
public bool DownloadClipsBookmarks { get => _downloadClipsBookmarks; set => this.RaiseAndSetIfChanged(ref _downloadClipsBookmarks, value); }
private bool _useWidevine, _requestSpatial, _request_xHE_AAC;
public bool UseWidevine { get => _useWidevine; set => this.RaiseAndSetIfChanged(ref _useWidevine, value); }
public bool Request_xHE_AAC { get => _request_xHE_AAC; set => this.RaiseAndSetIfChanged(ref _request_xHE_AAC, value); }
public bool RequestSpatial { get => _requestSpatial; set => this.RaiseAndSetIfChanged(ref _requestSpatial, value); }
public bool DownloadClipsBookmarks { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
public bool UseWidevine { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
public bool Request_xHE_AAC { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
public bool RequestSpatial { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
public EnumDisplay<Configuration.DownloadQuality> FileDownloadQuality { get; set; }
public EnumDisplay<Configuration.SpatialCodec> SpatialAudioCodec { get; set; }
@@ -158,10 +146,10 @@ namespace LibationAvalonia.ViewModels.Settings
public bool StripUnabridged { get; set; }
public string StripUnabridgedTip => Configuration.GetHelpText(nameof(StripUnabridged));
public bool DecryptToLossy {
get => _decryptToLossy;
get => field;
set
{
this.RaiseAndSetIfChanged(ref _decryptToLossy, value);
this.RaiseAndSetIfChanged(ref field, value);
if (DecryptToLossy && SpatialAudioCodec.Value is Configuration.SpatialCodec.AC_4)
{
SpatialAudioCodec = SpatialAudioCodecs[0];
@@ -176,20 +164,20 @@ namespace LibationAvalonia.ViewModels.Settings
public string LameDownsampleMonoTip => Configuration.GetHelpText(nameof(LameDownsampleMono));
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 bool SplitFilesByChapter { get => field; set { this.RaiseAndSetIfChanged(ref field, value); } }
public bool LameTargetBitrate { get => field; set { this.RaiseAndSetIfChanged(ref field, value); } }
public bool LameMatchSource { get => field; set { this.RaiseAndSetIfChanged(ref field, value); } }
public int LameBitrate { get => field; set { this.RaiseAndSetIfChanged(ref field, value); } }
public int LameVBRQuality { get => field; set { this.RaiseAndSetIfChanged(ref field, value); } }
public string ChapterTitleTemplate { get => _chapterTitleTemplate; set { this.RaiseAndSetIfChanged(ref _chapterTitleTemplate, value); } }
public bool AllowLibationFixup
{
get => _allowLibationFixup;
get => field;
set
{
if (!this.RaiseAndSetIfChanged(ref _allowLibationFixup, value))
if (!this.RaiseAndSetIfChanged(ref field, value))
{
SplitFilesByChapter = false;
StripAudibleBrandAudio = false;

View File

@@ -150,6 +150,8 @@ namespace LibationAvalonia.Views
{
productsDisplay?.CloseImageDisplay();
this.SaveSizeAndLocation(Configuration.Instance);
//This is double firing with 11.3.9
Closing -= MainWindow_Closing;
}
private void selectAndFocusSearchBox()

View File

@@ -327,7 +327,7 @@ namespace LibationAvalonia.Views
{
//No need to persist these changes. They only needs to last long for the files to start downloading
entry4.Book.UserDefinedItem.BookStatus = LiberatedStatus.NotLiberated;
if (entry4.Book.HasPdf())
if (entry4.Book.HasPdf)
entry4.Book.UserDefinedItem.SetPdfStatus(LiberatedStatus.NotLiberated);
LiberateClicked?.Invoke(this, [entry4.LibraryBook]);
})

View File

@@ -1,4 +1,5 @@
using System;
using Dinah.Core;
using System;
using System.IO;
namespace LibationCli;
@@ -12,10 +13,10 @@ internal class ConsoleProgressBar
public double? Progress
{
get => m_Progress;
get => field;
set
{
m_Progress = value ?? 0;
field = value ?? 0;
WriteProgress();
}
}
@@ -30,7 +31,6 @@ internal class ConsoleProgressBar
}
}
private double m_Progress;
private TimeSpan m_RemainingTime;
private int m_LastWriteLength = 0;
private const int MAX_ETA_LEN = 10;
@@ -51,7 +51,7 @@ internal class ConsoleProgressBar
private void WriteProgress()
{
var numCompleted = (int)Math.Round(double.Min(100, m_Progress) * m_NumProgressPieces / 100);
var numCompleted = (int)Math.Round(double.Min(100, Progress?? 0) * m_NumProgressPieces / 100);
var numRemaining = m_NumProgressPieces - numCompleted;
var progressBar = $"[{new string(ProgressChar, numCompleted)}{new string(NoProgressChar, numRemaining)}] ";

View File

@@ -19,8 +19,32 @@ namespace LibationFileManager
protected abstract List<LongPath> GetFilePathsCustom(string productId);
#region static
public static LongPath DownloadsInProgressDirectory => Directory.CreateDirectory(Path.Combine(Configuration.Instance.InProgress, "DownloadsInProgress")).FullName;
public static LongPath DecryptInProgressDirectory => Directory.CreateDirectory(Path.Combine(Configuration.Instance.InProgress, "DecryptInProgress")).FullName;
/*
* Operations like LibraryCommands.GetCounts() hit the file system hard.
* Since failing to create a directory and exception handling is expensive,
* only retry creating InProgress subdirectories every RetryInProgressInterval.
*/
private static DateTime lastInProgressFail;
private static readonly TimeSpan RetryInProgressInterval = TimeSpan.FromSeconds(2);
private static DirectoryInfo? CreateInProgressDirectory(string subDirectory)
{
try
{
return (DateTime.UtcNow - lastInProgressFail) < RetryInProgressInterval ? null
: new DirectoryInfo(Configuration.Instance.InProgress).CreateSubdirectoryEx(subDirectory);
}
catch (Exception ex)
{
Serilog.Log.Error(ex, "Error creating subdirectory in {@InProgress}", Configuration.Instance.InProgress);
lastInProgressFail = DateTime.UtcNow;
return null;
}
}
public static LongPath? DownloadsInProgressDirectory => CreateInProgressDirectory("DownloadsInProgress")?.FullName;
public static LongPath? DecryptInProgressDirectory => CreateInProgressDirectory("DecryptInProgress")?.FullName;
static AudibleFileStorage()
{
@@ -28,7 +52,9 @@ namespace LibationFileManager
//Do not clean DownloadsInProgressDirectory. Those files are resumable.
try
{
foreach (var tempFile in FileUtility.SaferEnumerateFiles(DecryptInProgressDirectory))
if (DecryptInProgressDirectory is not LongPath decryptDir)
return;
foreach (var tempFile in FileUtility.SaferEnumerateFiles(decryptDir))
FileUtility.SaferDelete(tempFile);
}
catch (Exception ex)
@@ -114,9 +140,12 @@ namespace LibationFileManager
protected override List<LongPath> GetFilePathsCustom(string productId)
{
if (DownloadsInProgressDirectory is not LongPath dlFolder)
return [];
var regex = GetBookSearchRegex(productId);
return FileUtility
.SaferEnumerateFiles(DownloadsInProgressDirectory, "*.*", SearchOption.AllDirectories)
.SaferEnumerateFiles(dlFolder, "*.*", SearchOption.AllDirectories)
.Where(s => regex.IsMatch(s)).ToList();
}

View File

@@ -86,11 +86,10 @@ namespace LibationFileManager.Templates
=> NamingTemplate?.TagsInUse.Cast<TemplateTags>() ?? Enumerable.Empty<TemplateTags>();
public string TemplateText => NamingTemplate?.TemplateText ?? "";
private readonly NamingTemplate? _namingTemplate;
protected NamingTemplate NamingTemplate
{
get => _namingTemplate ?? throw new NullReferenceException(nameof(_namingTemplate));
private init => _namingTemplate = value;
get => field ?? throw new NullReferenceException(nameof(NamingTemplate));
private init => field = value;
}
#endregion

View File

@@ -9,7 +9,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="LuceneNet303r2" Version="3.0.3.10" />
<PackageReference Include="LuceneNet303r2" Version="3.0.3.11" />
</ItemGroup>
<ItemGroup>

View File

@@ -37,8 +37,8 @@ namespace LibationSearchEngine
{ FieldType.ID, lb => lb.Book.AudibleProductId.ToLowerInvariant(), nameof(Book.AudibleProductId), "ProductId", "Id", "ASIN" },
{ FieldType.Raw, lb => lb.Book.AudibleProductId, _ID_ },
{ FieldType.String, lb => lb.Book.TitleWithSubtitle, "Title", "ProductId", "Id", "ASIN" },
{ FieldType.String, lb => lb.Book.AuthorNames(), "AuthorNames", "Author", "Authors" },
{ FieldType.String, lb => lb.Book.NarratorNames(), "NarratorNames", "Narrator", "Narrators" },
{ FieldType.String, lb => lb.Book.AuthorNames, "AuthorNames", "Author", "Authors" },
{ FieldType.String, lb => lb.Book.NarratorNames, "NarratorNames", "Narrator", "Narrators" },
{ FieldType.String, lb => lb.Book.Publisher, nameof(Book.Publisher) },
{ FieldType.String, lb => lb.Book.SeriesNames(), "SeriesNames", "Narrator", "Series" },
{ FieldType.String, lb => string.Join(", ", lb.Book.SeriesLink.Select(s => s.Series.AudibleSeriesId)), "SeriesId" },
@@ -48,7 +48,7 @@ namespace LibationSearchEngine
{ FieldType.String, lb => lb.Book.Locale, "Locale", "Region" },
{ FieldType.String, lb => lb.Account, "Account", "Email" },
{ FieldType.String, lb => lb.Book.UserDefinedItem.LastDownloadedFormat?.CodecString, "Codec", "DownloadedCodec" },
{ FieldType.Bool, lb => lb.Book.HasPdf().ToString(), "HasDownloads", "HasDownload", "Downloads" , "Download", "HasPDFs", "HasPDF" , "PDFs", "PDF" },
{ FieldType.Bool, lb => lb.Book.HasPdf.ToString(), "HasDownloads", "HasDownload", "Downloads" , "Download", "HasPDFs", "HasPDF" , "PDFs", "PDF" },
{ FieldType.Bool, lb => (lb.Book.UserDefinedItem.Rating.OverallRating > 0f).ToString(), "IsRated", "Rated" },
{ FieldType.Bool, lb => isAuthorNarrated(lb.Book).ToString(), "IsAuthorNarrated", "AuthorNarrated" },
{ FieldType.Bool, lb => lb.Book.IsAbridged.ToString(), nameof(Book.IsAbridged), "Abridged" },

View File

@@ -32,12 +32,12 @@ namespace LibationUiBase.GridView
public bool Expanded
{
get => expanded;
get => field;
set
{
if (value != expanded)
if (value != field)
{
expanded = value;
field = value;
Invalidate(nameof(Expanded), nameof(ButtonImage));
}
}
@@ -59,7 +59,6 @@ namespace LibationUiBase.GridView
private DateTime lastBookUpdate;
private LiberatedStatus bookStatus;
private bool expanded;
private readonly bool isAbsent;
private static readonly Dictionary<string, object> iconCache = new();

View File

@@ -60,7 +60,7 @@ public class GridContextMenu
.UpdateUserDefinedItemAsync(udi =>
{
udi.BookStatus = LiberatedStatus.Liberated;
if (udi.Book.HasPdf())
if (udi.Book.HasPdf)
udi.SetPdfStatus(LiberatedStatus.Liberated);
});
}
@@ -71,7 +71,7 @@ public class GridContextMenu
.UpdateUserDefinedItemAsync(udi =>
{
udi.BookStatus = LiberatedStatus.NotLiberated;
if (udi.Book.HasPdf())
if (udi.Book.HasPdf)
udi.SetPdfStatus(LiberatedStatus.NotLiberated);
});
}

View File

@@ -32,44 +32,27 @@ namespace LibationUiBase.GridView
#region Model properties exposed to the view
protected bool? remove = false;
private EntryStatus _liberate;
private string _purchasedate;
private string _length;
private LastDownloadStatus _lastDownload;
private Lazy<object> _lazyCover;
private string _series;
private SeriesOrder _seriesOrder;
private string _title;
private string _authors;
private string _narrators;
private string _category;
private string _misc;
private string _description;
private Rating _productrating;
private string _bookTags;
private Rating _myRating;
private bool _isSpatial;
private string _includedUntil;
private string _account;
public abstract bool? Remove { get; set; }
public EntryStatus Liberate { get => _liberate; private set => RaiseAndSetIfChanged(ref _liberate, value); }
public string PurchaseDate { get => _purchasedate; protected set => RaiseAndSetIfChanged(ref _purchasedate, value); }
public string Length { get => _length; protected set => RaiseAndSetIfChanged(ref _length, value); }
public LastDownloadStatus LastDownload { get => _lastDownload; protected set => RaiseAndSetIfChanged(ref _lastDownload, value); }
public EntryStatus Liberate { get => field; private set => RaiseAndSetIfChanged(ref field, value); }
public string PurchaseDate { get => field; protected set => RaiseAndSetIfChanged(ref field, value); }
public string Length { get => field; protected set => RaiseAndSetIfChanged(ref field, value); }
public LastDownloadStatus LastDownload { get => field; protected set => RaiseAndSetIfChanged(ref field, value); }
public object Cover { get => _lazyCover.Value; }
public string Series { get => _series; private set => RaiseAndSetIfChanged(ref _series, value); }
public SeriesOrder SeriesOrder { get => _seriesOrder; private set => RaiseAndSetIfChanged(ref _seriesOrder, value); }
public string Title { get => _title; private set => RaiseAndSetIfChanged(ref _title, value); }
public string Authors { get => _authors; private set => RaiseAndSetIfChanged(ref _authors, value); }
public string Narrators { get => _narrators; private set => RaiseAndSetIfChanged(ref _narrators, value); }
public string Category { get => _category; private set => RaiseAndSetIfChanged(ref _category, value); }
public string Misc { get => _misc; private set => RaiseAndSetIfChanged(ref _misc, value); }
public string Description { get => _description; private set => RaiseAndSetIfChanged(ref _description, value); }
public Rating ProductRating { get => _productrating; private set => RaiseAndSetIfChanged(ref _productrating, value); }
public string BookTags { get => _bookTags; private set => RaiseAndSetIfChanged(ref _bookTags, value); }
public bool IsSpatial { get => _isSpatial; protected set => RaiseAndSetIfChanged(ref _isSpatial, value); }
public string IncludedUntil { get => _includedUntil; protected set => RaiseAndSetIfChanged(ref _includedUntil, value); }
public string Account { get => _account; protected set => RaiseAndSetIfChanged(ref _account, value); }
public string Series { get => field; private set => RaiseAndSetIfChanged(ref field, value); }
public SeriesOrder SeriesOrder { get => field; private set => RaiseAndSetIfChanged(ref field, value); }
public string Title { get => field; private set => RaiseAndSetIfChanged(ref field, value); }
public string Authors { get => field; private set => RaiseAndSetIfChanged(ref field, value); }
public string Narrators { get => field; private set => RaiseAndSetIfChanged(ref field, value); }
public string Category { get => field; private set => RaiseAndSetIfChanged(ref field, value); }
public string Misc { get => field; private set => RaiseAndSetIfChanged(ref field, value); }
public string Description { get => field; private set => RaiseAndSetIfChanged(ref field, value); }
public Rating ProductRating { get => field; private set => RaiseAndSetIfChanged(ref field, value); }
public string BookTags { get => field; private set => RaiseAndSetIfChanged(ref field, value); }
public bool IsSpatial { get => field; protected set => RaiseAndSetIfChanged(ref field, value); }
public string IncludedUntil { get => field; protected set => RaiseAndSetIfChanged(ref field, value); }
public string Account { get => field; protected set => RaiseAndSetIfChanged(ref field, value); }
public Rating MyRating
{
@@ -115,8 +98,8 @@ namespace LibationUiBase.GridView
RaiseAndSetIfChanged(ref _myRating, Book.UserDefinedItem.Rating, nameof(MyRating));
PurchaseDate = GetPurchaseDateString();
ProductRating = Book.Rating ?? new Rating(0, 0, 0);
Authors = Book.AuthorNames();
Narrators = Book.NarratorNames();
Authors = Book.AuthorNames;
Narrators = Book.NarratorNames;
Category = string.Join(", ", Book.LowestCategoryNames());
Misc = GetMiscDisplay(libraryBook);
LastDownload = new(Book.UserDefinedItem);
@@ -323,7 +306,7 @@ namespace LibationUiBase.GridView
details.Add($"Account: {locale} - {acct}");
if (libraryBook.Book.HasPdf())
if (libraryBook.Book.HasPdf)
details.Add("Has PDF");
if (libraryBook.Book.IsAbridged)
details.Add("Abridged");

View File

@@ -44,26 +44,16 @@ public class ProcessBookViewModel : ReactiveObject
{
public LibraryBook LibraryBook { get; protected set; }
private ProcessBookResult _result = ProcessBookResult.None;
private ProcessBookStatus _status = ProcessBookStatus.Queued;
private string? _narrator;
private string? _author;
private string? _title;
private int _progress;
private string? _eta;
private object? _cover;
private TimeSpan _timeRemaining;
#region Properties exposed to the view
public ProcessBookResult Result { get => _result; set { RaiseAndSetIfChanged(ref _result, value); RaisePropertyChanged(nameof(StatusText)); } }
public ProcessBookStatus Status { get => _status; set { RaiseAndSetIfChanged(ref _status, value); RaisePropertyChanged(nameof(IsFinished)); RaisePropertyChanged(nameof(IsDownloading)); RaisePropertyChanged(nameof(Queued)); } }
public string? Narrator { get => _narrator; set => RaiseAndSetIfChanged(ref _narrator, value); }
public string? Author { get => _author; set => RaiseAndSetIfChanged(ref _author, value); }
public string? Title { get => _title; set => RaiseAndSetIfChanged(ref _title, value); }
public int Progress { get => _progress; protected set => RaiseAndSetIfChanged(ref _progress, value); }
public TimeSpan TimeRemaining { get => _timeRemaining; set { RaiseAndSetIfChanged(ref _timeRemaining, value); ETA = $"ETA: {value:mm\\:ss}"; } }
public string? ETA { get => _eta; private set => RaiseAndSetIfChanged(ref _eta, value); }
public object? Cover { get => _cover; protected set => RaiseAndSetIfChanged(ref _cover, value); }
public ProcessBookResult Result { get => field; set { RaiseAndSetIfChanged(ref field, value); RaisePropertyChanged(nameof(StatusText)); } }
public ProcessBookStatus Status { get => field; set { RaiseAndSetIfChanged(ref field, value); RaisePropertyChanged(nameof(IsFinished)); RaisePropertyChanged(nameof(IsDownloading)); RaisePropertyChanged(nameof(Queued)); } }
public string? Narrator { get => field; set => RaiseAndSetIfChanged(ref field, value); }
public string? Author { get => field; set => RaiseAndSetIfChanged(ref field, value); }
public string? Title { get => field; set => RaiseAndSetIfChanged(ref field, value); }
public int Progress { get => field; protected set => RaiseAndSetIfChanged(ref field, value); }
public TimeSpan TimeRemaining { get => field; set { RaiseAndSetIfChanged(ref field, value); ETA = $"ETA: {value:mm\\:ss}"; } }
public string? ETA { get => field; private set => RaiseAndSetIfChanged(ref field, value); }
public object? Cover { get => field; protected set => RaiseAndSetIfChanged(ref field, value); }
public bool IsFinished => Status is not ProcessBookStatus.Queued and not ProcessBookStatus.Working;
public bool IsDownloading => Status is ProcessBookStatus.Working;
public bool Queued => Status is ProcessBookStatus.Queued;
@@ -109,17 +99,16 @@ public class ProcessBookViewModel : ReactiveObject
{
LibraryBook = libraryBook;
_title = LibraryBook.Book.TitleWithSubtitle;
_author = LibraryBook.Book.AuthorNames();
_narrator = LibraryBook.Book.NarratorNames();
Title = LibraryBook.Book.TitleWithSubtitle;
Author = LibraryBook.Book.AuthorNames;
Narrator = LibraryBook.Book.NarratorNames;
(bool isDefault, byte[] picture) = PictureStorage.GetPicture(new PictureDefinition(LibraryBook.Book.PictureId, PictureSize._80x80));
if (isDefault)
PictureStorage.PictureCached += PictureStorage_PictureCached;
// Mutable property. Set the field so PropertyChanged isn't fired.
_cover = BaseUtil.LoadImage(picture, PictureSize._80x80);
Cover = BaseUtil.LoadImage(picture, PictureSize._80x80);
}
private void PictureStorage_PictureCached(object? sender, PictureCachedEventArgs e)
@@ -307,8 +296,8 @@ public class ProcessBookViewModel : ReactiveObject
LogInfo($"{Environment.NewLine}{processable.Name} Step, Begin: {libraryBook.Book}");
Title = libraryBook.Book.TitleWithSubtitle;
Author = libraryBook.Book.AuthorNames();
Narrator = libraryBook.Book.NarratorNames();
Author = libraryBook.Book.AuthorNames;
Narrator = libraryBook.Book.NarratorNames;
}
private async void Processable_Completed(object? sender, LibraryBook libraryBook)
@@ -386,8 +375,8 @@ public class ProcessBookViewModel : ReactiveObject
details = $"""
Title: {libraryBook.Book.TitleWithSubtitle}
ID: {libraryBook.Book.AudibleProductId}
Author: {trunc(libraryBook.Book.AuthorNames())}
Narr: {trunc(libraryBook.Book.NarratorNames())}
Author: {trunc(libraryBook.Book.AuthorNames)}
Narr: {trunc(libraryBook.Book.NarratorNames)}
""";
}
catch

View File

@@ -30,23 +30,18 @@ public class ProcessQueueViewModel : ReactiveObject
SpeedLimit = LibationFileManager.Configuration.Instance.DownloadSpeedLimit / 1024m / 1024;
}
private int _completedCount;
private int _errorCount;
private int _queuedCount;
private string? _runningTime;
private bool _progressBarVisible;
private decimal _speedLimit;
public int CompletedCount { get => _completedCount; private set { RaiseAndSetIfChanged(ref _completedCount, value); RaisePropertyChanged(nameof(AnyCompleted)); } }
public int QueuedCount { get => _queuedCount; private set { this.RaiseAndSetIfChanged(ref _queuedCount, value); RaisePropertyChanged(nameof(AnyQueued)); } }
public int ErrorCount { get => _errorCount; private set { RaiseAndSetIfChanged(ref _errorCount, value); RaisePropertyChanged(nameof(AnyErrors)); } }
public string? RunningTime { get => _runningTime; set => RaiseAndSetIfChanged(ref _runningTime, value); }
public bool ProgressBarVisible { get => _progressBarVisible; set => RaiseAndSetIfChanged(ref _progressBarVisible, value); }
public int CompletedCount { get => field; private set { RaiseAndSetIfChanged(ref field, value); RaisePropertyChanged(nameof(AnyCompleted)); } }
public int QueuedCount { get => field; private set { this.RaiseAndSetIfChanged(ref field, value); RaisePropertyChanged(nameof(AnyQueued)); } }
public int ErrorCount { get => field; private set { RaiseAndSetIfChanged(ref field, value); RaisePropertyChanged(nameof(AnyErrors)); } }
public string? RunningTime { get => field; set => RaiseAndSetIfChanged(ref field, value); }
public bool ProgressBarVisible { get => field; set => RaiseAndSetIfChanged(ref field, value); }
public bool AnyCompleted => CompletedCount > 0;
public bool AnyQueued => QueuedCount > 0;
public bool AnyErrors => ErrorCount > 0;
public double Progress => 100d * Queue.Completed.Count / Queue.Count;
public decimal SpeedLimitIncrement { get; private set; }
private decimal _speedLimit;
public decimal SpeedLimit
{
get => _speedLimit;
@@ -189,6 +184,26 @@ public class ProcessQueueViewModel : ReactiveObject
MessageBoxIcon.Error);
return false;
}
else if (AudibleFileStorage.DownloadsInProgressDirectory is null)
{
Serilog.Log.Logger.Error("Failed to create DownloadsInProgressDirectory in {@InProgress}", Configuration.Instance.InProgress);
MessageBoxBase.Show(
$"Libation was unable to create the \"Downloads In Progress\" folder in:\n{Configuration.Instance.InProgress}\n\nPlease change the In Progress location in the settings menu.",
"Failed to Create Downloads In Progress Directory",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
return false;
}
else if (AudibleFileStorage.DecryptInProgressDirectory is null)
{
Serilog.Log.Logger.Error("Failed to create DecryptInProgressDirectory in {@InProgress}", Configuration.Instance.InProgress);
MessageBoxBase.Show(
$"Libation was unable to create the \"Decrypt In Progress\" folder in:\n{Configuration.Instance.InProgress}\n\nPlease change the In Progress location in the settings menu.",
"Failed to Create Decrypt In Progress Directory",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
return false;
}
return true;
}

View File

@@ -4,8 +4,6 @@ namespace LibationWinForms
{
public class AccessibleDataGridViewButtonCell : DataGridViewButtonCell
{
private string accessibilityDescription;
protected string AccessibilityName { get; }
/// <summary>
@@ -13,10 +11,10 @@ namespace LibationWinForms
/// </summary>
protected string AccessibilityDescription
{
get => accessibilityDescription;
get => field;
set
{
accessibilityDescription = value;
field = value;
ToolTipText = value;
}
}

View File

@@ -4,8 +4,6 @@ namespace LibationWinForms
{
internal class AccessibleDataGridViewTextBoxCell : DataGridViewTextBoxCell
{
private string accessibilityDescription;
protected string AccessibilityName { get; }
/// <summary>
@@ -13,10 +11,10 @@ namespace LibationWinForms
/// </summary>
protected string AccessibilityDescription
{
get => accessibilityDescription;
get => field;
set
{
accessibilityDescription = value;
field = value;
ToolTipText = value;
}
}

View File

@@ -45,16 +45,16 @@ namespace LibationWinForms.Dialogs
this.coverPb.Image = WinFormsUtil.TryLoadImageOrDefault(picture, PictureSize._80x80);
var title = string.IsNullOrEmpty(Book.Subtitle) ? Book.Title : $"{Book.Title}\r\n {Book.Subtitle}";
var t = @$"
Title: {title}
Author(s): {Book.AuthorNames()}
Narrator(s): {Book.NarratorNames()}
Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")}
Category: {string.Join(", ", Book.LowestCategoryNames())}
Purchase Date: {_libraryBook.DateAdded:d}
Language: {Book.Language}
Audible ID: {Book.AudibleProductId}
".Trim();
var t = $"""
Title: {title}
Author(s): {Book.AuthorNames}
Narrator(s): {Book.NarratorNames}
Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")}
Category: {string.Join(", ", Book.LowestCategoryNames())}
Purchase Date: {_libraryBook.DateAdded:d}
Language: {Book.Language}
Audible ID: {Book.AudibleProductId}
""";
var seriesNames = Book.SeriesNames();
if (!string.IsNullOrWhiteSpace(seriesNames))

View File

@@ -253,9 +253,8 @@ namespace LibationWinForms.Dialogs
private class BookRecordEntry : LibationUiBase.ReactiveObject
{
private const string DateFormat = "yyyy-MM-dd HH\\:mm";
private bool _ischecked;
public IRecord Record { get; }
public bool IsChecked { get => _ischecked; set => RaiseAndSetIfChanged(ref _ischecked, value); }
public bool IsChecked { get => field; set => RaiseAndSetIfChanged(ref field, value); }
public string Type => Record.GetType().Name;
public string Start => formatTimeSpan(Record.Start);
public string Created => Record.Created.ToString(DateFormat);

View File

@@ -1,4 +1,6 @@
using Dinah.Core;
using AudibleApi;
using Dinah.Core;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;
using System;
using System.Windows.Forms;
@@ -19,15 +21,32 @@ namespace LibationWinForms.Login
Controls.Add(webView);
webView.NavigationStarting += WebView_NavigationStarting;
webView.CoreWebView2InitializationCompleted += WebView_CoreWebView2InitializationCompleted;
this.SetLibationIcon();
}
}
public WebLoginDialog(string accountID, string loginUrl) : this()
public WebLoginDialog(string accountID, ChoiceIn choiceIn) : this()
{
this.accountID = ArgumentValidator.EnsureNotNullOrWhiteSpace(accountID, nameof(accountID));
webView.Source = new Uri(ArgumentValidator.EnsureNotNullOrWhiteSpace(loginUrl, nameof(loginUrl)));
ArgumentValidator.EnsureNotNullOrWhiteSpace(choiceIn?.LoginUrl, nameof(choiceIn));
this.Load += async (_, _) =>
{
//enable private browsing
var env = await CoreWebView2Environment.CreateAsync();
var options = env.CreateCoreWebView2ControllerOptions();
options.IsInPrivateModeEnabled = true;
await webView.EnsureCoreWebView2Async(env, options);
webView.CoreWebView2.Settings.UserAgent = Resources.User_Agent;
//Load init cookies
foreach (System.Net.Cookie cookie in choiceIn.SignInCookies ?? [])
{
webView.CoreWebView2.CookieManager.AddOrUpdateCookie(webView.CoreWebView2.CookieManager.CreateCookieWithSystemNetCookie(cookie));
}
webView.CoreWebView2.DOMContentLoaded += CoreWebView2_DOMContentLoaded;
Invoke(() => webView.Source = new Uri(choiceIn.LoginUrl));
};
}
private void WebView_NavigationStarting(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationStartingEventArgs e)
@@ -40,13 +59,7 @@ namespace LibationWinForms.Login
}
}
private void WebView_CoreWebView2InitializationCompleted(object sender, Microsoft.Web.WebView2.Core.CoreWebView2InitializationCompletedEventArgs e)
{
webView.CoreWebView2.DOMContentLoaded -= CoreWebView2_DOMContentLoaded;
webView.CoreWebView2.DOMContentLoaded += CoreWebView2_DOMContentLoaded;
}
private async void CoreWebView2_DOMContentLoaded(object sender, Microsoft.Web.WebView2.Core.CoreWebView2DOMContentLoadedEventArgs e)
private async void CoreWebView2_DOMContentLoaded(object sender, CoreWebView2DOMContentLoadedEventArgs e)
{
await webView.ExecuteScriptAsync(getScript(accountID));
}

View File

@@ -28,7 +28,7 @@ namespace LibationWinForms.Login
{
try
{
using var weblogin = new WebLoginDialog(_account.AccountId, choiceIn.LoginUrl);
using var weblogin = new WebLoginDialog(_account.AccountId, choiceIn);
if (ShowDialog(weblogin))
return Task.FromResult(ChoiceOut.External(weblogin.ResponseUrl));
}

View File

@@ -31,7 +31,7 @@ namespace LibationWinForms
{
if (processBookQueue1.ViewModel.QueueDownloadDecrypt(libraryBooks))
SetQueueCollapseState(false);
else if (libraryBooks.Length == 1 && libraryBooks[0].Book.Audio_Exists())
else if (libraryBooks.Length == 1 && libraryBooks[0].Book.AudioExists)
{
// liberated: open explorer to file
var filePath = AudibleFileStorage.Audio.GetPath(libraryBooks[0].Book.AudibleProductId);

View File

@@ -7,18 +7,16 @@ namespace LibationWinForms.GridView
{
public partial class DescriptionDisplay : Form
{
private int borderThickness = 5;
public int BorderThickness
{
get => borderThickness;
get => field;
set
{
borderThickness = value;
textBox1.Location = new Point(borderThickness, borderThickness);
textBox1.Size = new Size(Width - 2 * borderThickness, Height - 2 * borderThickness);
field = value;
textBox1.Location = new Point(field, field);
textBox1.Size = new Size(Width - 2 * field, Height - 2 * field);
}
}
} = 5;
public string DescriptionText { get => textBox1.Text; set => textBox1.Text = value; }
public Point SpawnLocation { get; set; }
public DescriptionDisplay()

View File

@@ -13,24 +13,23 @@ namespace LibationWinForms.GridView
private const string SOLID_STAR = "★";
private const string HOLLOW_STAR = "☆";
private Rating _rating;
public Rating Rating
{
get => _rating;
get => field;
set
{
_rating = value;
field = value;
int rating = 0;
foreach (NoBorderLabel star in panelOverall.Controls)
star.Tag = star.Text = _rating.OverallRating > rating++ ? SOLID_STAR : HOLLOW_STAR;
star.Tag = star.Text = field.OverallRating > rating++ ? SOLID_STAR : HOLLOW_STAR;
rating = 0;
foreach (NoBorderLabel star in panelPerform.Controls)
star.Tag = star.Text = _rating.PerformanceRating > rating++ ? SOLID_STAR : HOLLOW_STAR;
star.Tag = star.Text = field.PerformanceRating > rating++ ? SOLID_STAR : HOLLOW_STAR;
rating = 0;
foreach (NoBorderLabel star in panelStory.Controls)
star.Tag = star.Text = _rating.StoryRating > rating++ ? SOLID_STAR : HOLLOW_STAR;
star.Tag = star.Text = field.StoryRating > rating++ ? SOLID_STAR : HOLLOW_STAR;
}
}

View File

@@ -219,7 +219,7 @@ namespace LibationWinForms.GridView
{
//No need to persist these changes. They only needs to last long for the files to start downloading
entry4.Book.UserDefinedItem.BookStatus = LiberatedStatus.NotLiberated;
if (entry4.Book.HasPdf())
if (entry4.Book.HasPdf)
entry4.Book.UserDefinedItem.SetPdfStatus(LiberatedStatus.NotLiberated);
LiberateClicked?.Invoke(s, [entry4.LibraryBook]);

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
<!-- Needed for RTM build of Npgsql.EntityFrameworkCore.PostgreSQL -->
<add key="myget" value="https://www.myget.org/F/npgsql-vnext/api/v3/index.json " />
</packageSources>
</configuration>