Null safety and minor UI bugfix
Properly cancel the Locate Audiobooks when the dialog window closes before scanning is finished.
This commit is contained in:
parent
29be091a4b
commit
db588629c0
@ -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<LibraryBook> library, string productId)
|
||||
public static LibraryBook? GetLibraryBook(this IQueryable<LibraryBook> library, string productId)
|
||||
=> library
|
||||
.GetLibrary()
|
||||
.SingleOrDefault(lb => lb.Book.AudibleProductId == productId);
|
||||
|
||||
@ -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<FilePathCache.CacheEntry> FileFound;
|
||||
private event EventHandler<FilePathCache.CacheEntry>? FileFound;
|
||||
private readonly CancellationTokenSource tokenSource = new();
|
||||
private readonly List<string> 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<string, string>($"[{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);
|
||||
|
||||
@ -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<ProcessBookStatus> ProcessBookStatusProperty =
|
||||
AvaloniaProperty.Register<ProcessBookControl, ProcessBookStatus>(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);
|
||||
|
||||
@ -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<ProcessBookViewModel> 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();
|
||||
|
||||
|
||||
@ -62,25 +62,22 @@ namespace LibationAvalonia.Views
|
||||
if (Design.IsDesignMode)
|
||||
{
|
||||
using var context = DbContexts.GetContext();
|
||||
List<LibraryBook> 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<LibraryBook>().ToList());
|
||||
DataContext = pdvm;
|
||||
|
||||
setGridScale(1);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user