diff --git a/Source/DataLayer/QueryObjects/LibraryBookQueries.cs b/Source/DataLayer/QueryObjects/LibraryBookQueries.cs index deca475b..4a3e60e1 100644 --- a/Source/DataLayer/QueryObjects/LibraryBookQueries.cs +++ b/Source/DataLayer/QueryObjects/LibraryBookQueries.cs @@ -2,6 +2,7 @@ using System.Linq; using Microsoft.EntityFrameworkCore; +#nullable enable namespace DataLayer { // only library importing should use tracking. All else should be NoTracking. @@ -24,13 +25,13 @@ namespace DataLayer .Where(c => !c.Book.IsEpisodeParent() || includeParents) .ToList(); - public static LibraryBook GetLibraryBook_Flat_NoTracking(this LibationContext context, string productId) + public static LibraryBook? GetLibraryBook_Flat_NoTracking(this LibationContext context, string productId) => context .LibraryBooks .AsNoTrackingWithIdentityResolution() .GetLibraryBook(productId); - public static LibraryBook GetLibraryBook(this IQueryable library, string productId) + public static LibraryBook? GetLibraryBook(this IQueryable library, string productId) => library .GetLibrary() .SingleOrDefault(lb => lb.Book.AudibleProductId == productId); diff --git a/Source/LibationAvalonia/Dialogs/LocateAudiobooksDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/LocateAudiobooksDialog.axaml.cs index fe0618de..f1eb4c75 100644 --- a/Source/LibationAvalonia/Dialogs/LocateAudiobooksDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/LocateAudiobooksDialog.axaml.cs @@ -13,11 +13,12 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +#nullable enable namespace LibationAvalonia.Dialogs { public partial class LocateAudiobooksDialog : DialogWindow { - private event EventHandler FileFound; + private event EventHandler? FileFound; private readonly CancellationTokenSource tokenSource = new(); private readonly List foundAsins = new(); private readonly LocatedAudiobooksViewModel _viewModel; @@ -41,7 +42,7 @@ namespace LibationAvalonia.Dialogs } } - private void LocateAudiobooksDialog_Closing(object sender, System.ComponentModel.CancelEventArgs e) + private void LocateAudiobooksDialog_Closing(object? sender, System.ComponentModel.CancelEventArgs e) { tokenSource.Cancel(); //If this dialog is closed before it's completed, Closing is fired @@ -50,7 +51,7 @@ namespace LibationAvalonia.Dialogs this.SaveSizeAndLocation(Configuration.Instance); } - private void LocateAudiobooks_FileFound(object sender, FilePathCache.CacheEntry e) + private void LocateAudiobooks_FileFound(object? sender, FilePathCache.CacheEntry e) { var newItem = new Tuple($"[{e.Id}]", Path.GetFileName(e.Path)); _viewModel.FoundFiles.Add(newItem); @@ -63,13 +64,13 @@ namespace LibationAvalonia.Dialogs } } - private async void LocateAudiobooksDialog_Opened(object sender, EventArgs e) + private async void LocateAudiobooksDialog_Opened(object? sender, EventArgs e) { var folderPicker = new FolderPickerOpenOptions { Title = "Select the folder to search for audiobooks", AllowMultiple = false, - SuggestedStartLocation = await StorageProvider.TryGetFolderFromPathAsync(Configuration.Instance.Books.PathWithoutPrefix) + SuggestedStartLocation = await StorageProvider.TryGetFolderFromPathAsync(Configuration.Instance.Books?.PathWithoutPrefix ?? "") }; var selectedFolder = (await StorageProvider.OpenFolderPickerAsync(folderPicker))?.SingleOrDefault()?.TryGetLocalPath(); @@ -89,11 +90,13 @@ namespace LibationAvalonia.Dialogs FilePathCache.Insert(book); var lb = context.GetLibraryBook_Flat_NoTracking(book.Id); - if (lb?.Book?.UserDefinedItem.BookStatus is not LiberatedStatus.Liberated) + if (lb is not null && lb.Book?.UserDefinedItem.BookStatus is not LiberatedStatus.Liberated) await Task.Run(() => lb.UpdateBookStatus(LiberatedStatus.Liberated)); + tokenSource.Token.ThrowIfCancellationRequested(); FileFound?.Invoke(this, book); } + catch (OperationCanceledException) { } catch (Exception ex) { Serilog.Log.Error(ex, "Error adding found audiobook file to Libation. {@audioFile}", book); diff --git a/Source/LibationAvalonia/Views/ProcessBookControl.axaml.cs b/Source/LibationAvalonia/Views/ProcessBookControl.axaml.cs index f7adccf5..8e877518 100644 --- a/Source/LibationAvalonia/Views/ProcessBookControl.axaml.cs +++ b/Source/LibationAvalonia/Views/ProcessBookControl.axaml.cs @@ -5,14 +5,15 @@ using DataLayer; using LibationUiBase; using LibationUiBase.ProcessQueue; +#nullable enable namespace LibationAvalonia.Views { - public delegate void QueueItemPositionButtonClicked(ProcessBookViewModel item, QueuePosition queueButton); - public delegate void QueueItemCancelButtonClicked(ProcessBookViewModel item); + public delegate void QueueItemPositionButtonClicked(ProcessBookViewModel? item, QueuePosition queueButton); + public delegate void QueueItemCancelButtonClicked(ProcessBookViewModel? item); public partial class ProcessBookControl : UserControl { - public static event QueueItemPositionButtonClicked PositionButtonClicked; - public static event QueueItemCancelButtonClicked CancelButtonClicked; + public static event QueueItemPositionButtonClicked? PositionButtonClicked; + public static event QueueItemCancelButtonClicked? CancelButtonClicked; public static readonly StyledProperty ProcessBookStatusProperty = AvaloniaProperty.Register(nameof(ProcessBookStatus), enableDataValidation: true); @@ -31,12 +32,13 @@ namespace LibationAvalonia.Views { using var context = DbContexts.GetContext(); ViewModels.MainVM.Configure_NonUI(); - DataContext = new ProcessBookViewModel(context.GetLibraryBook_Flat_NoTracking("B017V4IM1G")); + if (context.GetLibraryBook_Flat_NoTracking("B017V4IM1G") is LibraryBook book) + DataContext = new ProcessBookViewModel(book); return; } } - private ProcessBookViewModel DataItem => DataContext is null ? null : DataContext as ProcessBookViewModel; + private ProcessBookViewModel? DataItem => DataContext as ProcessBookViewModel; public void Cancel_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) => CancelButtonClicked?.Invoke(DataItem); diff --git a/Source/LibationAvalonia/Views/ProcessQueueControl.axaml.cs b/Source/LibationAvalonia/Views/ProcessQueueControl.axaml.cs index 35333cf4..9a67905f 100644 --- a/Source/LibationAvalonia/Views/ProcessQueueControl.axaml.cs +++ b/Source/LibationAvalonia/Views/ProcessQueueControl.axaml.cs @@ -34,44 +34,51 @@ namespace LibationAvalonia.Views var vm = new ProcessQueueViewModel(); DataContext = vm; using var context = DbContexts.GetContext(); + + + var trialBook = context.GetLibraryBook_Flat_NoTracking("B017V4IM1G") ?? context.GetLibrary_Flat_NoTracking().FirstOrDefault(); + if (trialBook is null) + return; + + List testList = new() { - new ProcessBookViewModel(context.GetLibraryBook_Flat_NoTracking("B017V4IM1G")) + new ProcessBookViewModel(trialBook) { Result = ProcessBookResult.FailedAbort, Status = ProcessBookStatus.Failed, }, - new ProcessBookViewModel(context.GetLibraryBook_Flat_NoTracking("B017V4IWVG")) + new ProcessBookViewModel(trialBook) { Result = ProcessBookResult.FailedSkip, Status = ProcessBookStatus.Failed, }, - new ProcessBookViewModel(context.GetLibraryBook_Flat_NoTracking("B017V4JA2Q")) + new ProcessBookViewModel(trialBook) { Result = ProcessBookResult.FailedRetry, Status = ProcessBookStatus.Failed, }, - new ProcessBookViewModel(context.GetLibraryBook_Flat_NoTracking("B017V4NUPO")) + new ProcessBookViewModel(trialBook) { Result = ProcessBookResult.ValidationFail, Status = ProcessBookStatus.Failed, }, - new ProcessBookViewModel(context.GetLibraryBook_Flat_NoTracking("B017V4NMX4")) + new ProcessBookViewModel(trialBook) { Result = ProcessBookResult.Cancelled, Status = ProcessBookStatus.Cancelled, }, - new ProcessBookViewModel(context.GetLibraryBook_Flat_NoTracking("B017V4NOZ0")) + new ProcessBookViewModel(trialBook) { Result = ProcessBookResult.Success, Status = ProcessBookStatus.Completed, }, - new ProcessBookViewModel(context.GetLibraryBook_Flat_NoTracking("B017WJ5ZK6")) + new ProcessBookViewModel(trialBook) { Result = ProcessBookResult.None, Status = ProcessBookStatus.Working, }, - new ProcessBookViewModel(context.GetLibraryBook_Flat_NoTracking("B017V4IM1G")) + new ProcessBookViewModel(trialBook) { Result = ProcessBookResult.None, Status = ProcessBookStatus.Queued, @@ -99,7 +106,7 @@ namespace LibationAvalonia.Views #region Control event handlers - private async void ProcessBookControl2_CancelButtonClicked(ProcessBookViewModel item) + private async void ProcessBookControl2_CancelButtonClicked(ProcessBookViewModel? item) { if (item is not null) { @@ -108,19 +115,20 @@ namespace LibationAvalonia.Views } } - private void ProcessBookControl2_ButtonClicked(ProcessBookViewModel item, QueuePosition queueButton) + private void ProcessBookControl2_ButtonClicked(ProcessBookViewModel? item, QueuePosition queueButton) { - Queue?.MoveQueuePosition(item, queueButton); + if (item is not null) + Queue?.MoveQueuePosition(item, queueButton); } - public async void CancelAllBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) + public async void CancelAllBtn_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { Queue?.ClearQueue(); if (Queue?.Current is not null) await Queue.Current.CancelAsync(); } - public void ClearFinishedBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) + public void ClearFinishedBtn_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { Queue?.ClearCompleted(); @@ -128,12 +136,12 @@ namespace LibationAvalonia.Views _viewModel.RunningTime = string.Empty; } - public void ClearLogBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) + public void ClearLogBtn_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { _viewModel?.LogEntries.Clear(); } - private async void LogCopyBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) + private async void LogCopyBtn_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { if (_viewModel is ProcessQueueViewModel vm) { @@ -143,14 +151,14 @@ namespace LibationAvalonia.Views } } - private async void cancelAllBtn_Click(object sender, EventArgs e) + private async void cancelAllBtn_Click(object? sender, EventArgs e) { Queue?.ClearQueue(); if (Queue?.Current is not null) await Queue.Current.CancelAsync(); } - private void btnClearFinished_Click(object sender, EventArgs e) + private void btnClearFinished_Click(object? sender, EventArgs e) { Queue?.ClearCompleted(); diff --git a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs index e9622024..735c22ad 100644 --- a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs +++ b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs @@ -62,25 +62,22 @@ namespace LibationAvalonia.Views if (Design.IsDesignMode) { using var context = DbContexts.GetContext(); - List sampleEntries; + LibraryBook?[] sampleEntries; try { - sampleEntries = new() - { - //context.GetLibraryBook_Flat_NoTracking("B00DCD0OXU"),try{ + sampleEntries = [ context.GetLibraryBook_Flat_NoTracking("B017WJ5ZK6"), context.GetLibraryBook_Flat_NoTracking("B017V4IWVG"), context.GetLibraryBook_Flat_NoTracking("B017V4JA2Q"), context.GetLibraryBook_Flat_NoTracking("B017V4NUPO"), context.GetLibraryBook_Flat_NoTracking("B017V4NMX4"), context.GetLibraryBook_Flat_NoTracking("B017V4NOZ0"), - context.GetLibraryBook_Flat_NoTracking("B017WJ5ZK6") - }; + context.GetLibraryBook_Flat_NoTracking("B017WJ5ZK6")]; } - catch { sampleEntries = new(); } + catch { sampleEntries = []; } var pdvm = new ProductsDisplayViewModel(); - _ = pdvm.BindToGridAsync(sampleEntries); + _ = pdvm.BindToGridAsync(sampleEntries.OfType().ToList()); DataContext = pdvm; setGridScale(1); diff --git a/Source/LibationFileManager/Configuration.LibationFiles.cs b/Source/LibationFileManager/Configuration.LibationFiles.cs index 875c1dc7..63a2cb1b 100644 --- a/Source/LibationFileManager/Configuration.LibationFiles.cs +++ b/Source/LibationFileManager/Configuration.LibationFiles.cs @@ -84,7 +84,7 @@ namespace LibationFileManager ProcessDirectory, LocalAppData, UserProfile, - Path.Combine(Path.GetTempPath(), "Libation") + WinTemp, }; //Try to find and validate appsettings.json in each folder @@ -181,7 +181,7 @@ namespace LibationFileManager } catch (Exception e) { - Serilog.Log.Error(e, "Failed to run shell command. {Arguments}", psi.ArgumentList); + Serilog.Log.Error(e, "Failed to run shell command. {@Arguments}", psi.ArgumentList); return null; } } diff --git a/Source/LibationWinForms/Dialogs/LocateAudiobooksDialog.cs b/Source/LibationWinForms/Dialogs/LocateAudiobooksDialog.cs index abaca53d..88e3c552 100644 --- a/Source/LibationWinForms/Dialogs/LocateAudiobooksDialog.cs +++ b/Source/LibationWinForms/Dialogs/LocateAudiobooksDialog.cs @@ -83,9 +83,11 @@ namespace LibationWinForms.Dialogs if (lb.Book.UserDefinedItem.BookStatus is not LiberatedStatus.Liberated) await Task.Run(() => lb.UpdateBookStatus(LiberatedStatus.Liberated)); + tokenSource.Token.ThrowIfCancellationRequested(); this.Invoke(FileFound, this, book); } - catch(Exception ex) + catch (OperationCanceledException) { } + catch (Exception ex) { Serilog.Log.Error(ex, "Error adding found audiobook file to Libation. {@audioFile}", book); }