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 System.Linq;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
namespace DataLayer
|
namespace DataLayer
|
||||||
{
|
{
|
||||||
// only library importing should use tracking. All else should be NoTracking.
|
// only library importing should use tracking. All else should be NoTracking.
|
||||||
@ -24,13 +25,13 @@ namespace DataLayer
|
|||||||
.Where(c => !c.Book.IsEpisodeParent() || includeParents)
|
.Where(c => !c.Book.IsEpisodeParent() || includeParents)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
public static LibraryBook GetLibraryBook_Flat_NoTracking(this LibationContext context, string productId)
|
public static LibraryBook? GetLibraryBook_Flat_NoTracking(this LibationContext context, string productId)
|
||||||
=> context
|
=> context
|
||||||
.LibraryBooks
|
.LibraryBooks
|
||||||
.AsNoTrackingWithIdentityResolution()
|
.AsNoTrackingWithIdentityResolution()
|
||||||
.GetLibraryBook(productId);
|
.GetLibraryBook(productId);
|
||||||
|
|
||||||
public static LibraryBook GetLibraryBook(this IQueryable<LibraryBook> library, string productId)
|
public static LibraryBook? GetLibraryBook(this IQueryable<LibraryBook> library, string productId)
|
||||||
=> library
|
=> library
|
||||||
.GetLibrary()
|
.GetLibrary()
|
||||||
.SingleOrDefault(lb => lb.Book.AudibleProductId == productId);
|
.SingleOrDefault(lb => lb.Book.AudibleProductId == productId);
|
||||||
|
|||||||
@ -13,11 +13,12 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
namespace LibationAvalonia.Dialogs
|
namespace LibationAvalonia.Dialogs
|
||||||
{
|
{
|
||||||
public partial class LocateAudiobooksDialog : DialogWindow
|
public partial class LocateAudiobooksDialog : DialogWindow
|
||||||
{
|
{
|
||||||
private event EventHandler<FilePathCache.CacheEntry> FileFound;
|
private event EventHandler<FilePathCache.CacheEntry>? FileFound;
|
||||||
private readonly CancellationTokenSource tokenSource = new();
|
private readonly CancellationTokenSource tokenSource = new();
|
||||||
private readonly List<string> foundAsins = new();
|
private readonly List<string> foundAsins = new();
|
||||||
private readonly LocatedAudiobooksViewModel _viewModel;
|
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();
|
tokenSource.Cancel();
|
||||||
//If this dialog is closed before it's completed, Closing is fired
|
//If this dialog is closed before it's completed, Closing is fired
|
||||||
@ -50,7 +51,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
this.SaveSizeAndLocation(Configuration.Instance);
|
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));
|
var newItem = new Tuple<string, string>($"[{e.Id}]", Path.GetFileName(e.Path));
|
||||||
_viewModel.FoundFiles.Add(newItem);
|
_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
|
var folderPicker = new FolderPickerOpenOptions
|
||||||
{
|
{
|
||||||
Title = "Select the folder to search for audiobooks",
|
Title = "Select the folder to search for audiobooks",
|
||||||
AllowMultiple = false,
|
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();
|
var selectedFolder = (await StorageProvider.OpenFolderPickerAsync(folderPicker))?.SingleOrDefault()?.TryGetLocalPath();
|
||||||
@ -89,11 +90,13 @@ namespace LibationAvalonia.Dialogs
|
|||||||
FilePathCache.Insert(book);
|
FilePathCache.Insert(book);
|
||||||
|
|
||||||
var lb = context.GetLibraryBook_Flat_NoTracking(book.Id);
|
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));
|
await Task.Run(() => lb.UpdateBookStatus(LiberatedStatus.Liberated));
|
||||||
|
|
||||||
|
tokenSource.Token.ThrowIfCancellationRequested();
|
||||||
FileFound?.Invoke(this, book);
|
FileFound?.Invoke(this, book);
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException) { }
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Serilog.Log.Error(ex, "Error adding found audiobook file to Libation. {@audioFile}", book);
|
Serilog.Log.Error(ex, "Error adding found audiobook file to Libation. {@audioFile}", book);
|
||||||
|
|||||||
@ -5,14 +5,15 @@ using DataLayer;
|
|||||||
using LibationUiBase;
|
using LibationUiBase;
|
||||||
using LibationUiBase.ProcessQueue;
|
using LibationUiBase.ProcessQueue;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
namespace LibationAvalonia.Views
|
namespace LibationAvalonia.Views
|
||||||
{
|
{
|
||||||
public delegate void QueueItemPositionButtonClicked(ProcessBookViewModel item, QueuePosition queueButton);
|
public delegate void QueueItemPositionButtonClicked(ProcessBookViewModel? item, QueuePosition queueButton);
|
||||||
public delegate void QueueItemCancelButtonClicked(ProcessBookViewModel item);
|
public delegate void QueueItemCancelButtonClicked(ProcessBookViewModel? item);
|
||||||
public partial class ProcessBookControl : UserControl
|
public partial class ProcessBookControl : UserControl
|
||||||
{
|
{
|
||||||
public static event QueueItemPositionButtonClicked PositionButtonClicked;
|
public static event QueueItemPositionButtonClicked? PositionButtonClicked;
|
||||||
public static event QueueItemCancelButtonClicked CancelButtonClicked;
|
public static event QueueItemCancelButtonClicked? CancelButtonClicked;
|
||||||
|
|
||||||
public static readonly StyledProperty<ProcessBookStatus> ProcessBookStatusProperty =
|
public static readonly StyledProperty<ProcessBookStatus> ProcessBookStatusProperty =
|
||||||
AvaloniaProperty.Register<ProcessBookControl, ProcessBookStatus>(nameof(ProcessBookStatus), enableDataValidation: true);
|
AvaloniaProperty.Register<ProcessBookControl, ProcessBookStatus>(nameof(ProcessBookStatus), enableDataValidation: true);
|
||||||
@ -31,12 +32,13 @@ namespace LibationAvalonia.Views
|
|||||||
{
|
{
|
||||||
using var context = DbContexts.GetContext();
|
using var context = DbContexts.GetContext();
|
||||||
ViewModels.MainVM.Configure_NonUI();
|
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;
|
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)
|
public void Cancel_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
=> CancelButtonClicked?.Invoke(DataItem);
|
=> CancelButtonClicked?.Invoke(DataItem);
|
||||||
|
|||||||
@ -34,44 +34,51 @@ namespace LibationAvalonia.Views
|
|||||||
var vm = new ProcessQueueViewModel();
|
var vm = new ProcessQueueViewModel();
|
||||||
DataContext = vm;
|
DataContext = vm;
|
||||||
using var context = DbContexts.GetContext();
|
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()
|
List<ProcessBookViewModel> testList = new()
|
||||||
{
|
{
|
||||||
new ProcessBookViewModel(context.GetLibraryBook_Flat_NoTracking("B017V4IM1G"))
|
new ProcessBookViewModel(trialBook)
|
||||||
{
|
{
|
||||||
Result = ProcessBookResult.FailedAbort,
|
Result = ProcessBookResult.FailedAbort,
|
||||||
Status = ProcessBookStatus.Failed,
|
Status = ProcessBookStatus.Failed,
|
||||||
},
|
},
|
||||||
new ProcessBookViewModel(context.GetLibraryBook_Flat_NoTracking("B017V4IWVG"))
|
new ProcessBookViewModel(trialBook)
|
||||||
{
|
{
|
||||||
Result = ProcessBookResult.FailedSkip,
|
Result = ProcessBookResult.FailedSkip,
|
||||||
Status = ProcessBookStatus.Failed,
|
Status = ProcessBookStatus.Failed,
|
||||||
},
|
},
|
||||||
new ProcessBookViewModel(context.GetLibraryBook_Flat_NoTracking("B017V4JA2Q"))
|
new ProcessBookViewModel(trialBook)
|
||||||
{
|
{
|
||||||
Result = ProcessBookResult.FailedRetry,
|
Result = ProcessBookResult.FailedRetry,
|
||||||
Status = ProcessBookStatus.Failed,
|
Status = ProcessBookStatus.Failed,
|
||||||
},
|
},
|
||||||
new ProcessBookViewModel(context.GetLibraryBook_Flat_NoTracking("B017V4NUPO"))
|
new ProcessBookViewModel(trialBook)
|
||||||
{
|
{
|
||||||
Result = ProcessBookResult.ValidationFail,
|
Result = ProcessBookResult.ValidationFail,
|
||||||
Status = ProcessBookStatus.Failed,
|
Status = ProcessBookStatus.Failed,
|
||||||
},
|
},
|
||||||
new ProcessBookViewModel(context.GetLibraryBook_Flat_NoTracking("B017V4NMX4"))
|
new ProcessBookViewModel(trialBook)
|
||||||
{
|
{
|
||||||
Result = ProcessBookResult.Cancelled,
|
Result = ProcessBookResult.Cancelled,
|
||||||
Status = ProcessBookStatus.Cancelled,
|
Status = ProcessBookStatus.Cancelled,
|
||||||
},
|
},
|
||||||
new ProcessBookViewModel(context.GetLibraryBook_Flat_NoTracking("B017V4NOZ0"))
|
new ProcessBookViewModel(trialBook)
|
||||||
{
|
{
|
||||||
Result = ProcessBookResult.Success,
|
Result = ProcessBookResult.Success,
|
||||||
Status = ProcessBookStatus.Completed,
|
Status = ProcessBookStatus.Completed,
|
||||||
},
|
},
|
||||||
new ProcessBookViewModel(context.GetLibraryBook_Flat_NoTracking("B017WJ5ZK6"))
|
new ProcessBookViewModel(trialBook)
|
||||||
{
|
{
|
||||||
Result = ProcessBookResult.None,
|
Result = ProcessBookResult.None,
|
||||||
Status = ProcessBookStatus.Working,
|
Status = ProcessBookStatus.Working,
|
||||||
},
|
},
|
||||||
new ProcessBookViewModel(context.GetLibraryBook_Flat_NoTracking("B017V4IM1G"))
|
new ProcessBookViewModel(trialBook)
|
||||||
{
|
{
|
||||||
Result = ProcessBookResult.None,
|
Result = ProcessBookResult.None,
|
||||||
Status = ProcessBookStatus.Queued,
|
Status = ProcessBookStatus.Queued,
|
||||||
@ -99,7 +106,7 @@ namespace LibationAvalonia.Views
|
|||||||
|
|
||||||
#region Control event handlers
|
#region Control event handlers
|
||||||
|
|
||||||
private async void ProcessBookControl2_CancelButtonClicked(ProcessBookViewModel item)
|
private async void ProcessBookControl2_CancelButtonClicked(ProcessBookViewModel? item)
|
||||||
{
|
{
|
||||||
if (item is not null)
|
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();
|
Queue?.ClearQueue();
|
||||||
if (Queue?.Current is not null)
|
if (Queue?.Current is not null)
|
||||||
await Queue.Current.CancelAsync();
|
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();
|
Queue?.ClearCompleted();
|
||||||
|
|
||||||
@ -128,12 +136,12 @@ namespace LibationAvalonia.Views
|
|||||||
_viewModel.RunningTime = string.Empty;
|
_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();
|
_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)
|
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();
|
Queue?.ClearQueue();
|
||||||
if (Queue?.Current is not null)
|
if (Queue?.Current is not null)
|
||||||
await Queue.Current.CancelAsync();
|
await Queue.Current.CancelAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnClearFinished_Click(object sender, EventArgs e)
|
private void btnClearFinished_Click(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
Queue?.ClearCompleted();
|
Queue?.ClearCompleted();
|
||||||
|
|
||||||
|
|||||||
@ -62,25 +62,22 @@ namespace LibationAvalonia.Views
|
|||||||
if (Design.IsDesignMode)
|
if (Design.IsDesignMode)
|
||||||
{
|
{
|
||||||
using var context = DbContexts.GetContext();
|
using var context = DbContexts.GetContext();
|
||||||
List<LibraryBook> sampleEntries;
|
LibraryBook?[] sampleEntries;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
sampleEntries = new()
|
sampleEntries = [
|
||||||
{
|
|
||||||
//context.GetLibraryBook_Flat_NoTracking("B00DCD0OXU"),try{
|
|
||||||
context.GetLibraryBook_Flat_NoTracking("B017WJ5ZK6"),
|
context.GetLibraryBook_Flat_NoTracking("B017WJ5ZK6"),
|
||||||
context.GetLibraryBook_Flat_NoTracking("B017V4IWVG"),
|
context.GetLibraryBook_Flat_NoTracking("B017V4IWVG"),
|
||||||
context.GetLibraryBook_Flat_NoTracking("B017V4JA2Q"),
|
context.GetLibraryBook_Flat_NoTracking("B017V4JA2Q"),
|
||||||
context.GetLibraryBook_Flat_NoTracking("B017V4NUPO"),
|
context.GetLibraryBook_Flat_NoTracking("B017V4NUPO"),
|
||||||
context.GetLibraryBook_Flat_NoTracking("B017V4NMX4"),
|
context.GetLibraryBook_Flat_NoTracking("B017V4NMX4"),
|
||||||
context.GetLibraryBook_Flat_NoTracking("B017V4NOZ0"),
|
context.GetLibraryBook_Flat_NoTracking("B017V4NOZ0"),
|
||||||
context.GetLibraryBook_Flat_NoTracking("B017WJ5ZK6")
|
context.GetLibraryBook_Flat_NoTracking("B017WJ5ZK6")];
|
||||||
};
|
|
||||||
}
|
}
|
||||||
catch { sampleEntries = new(); }
|
catch { sampleEntries = []; }
|
||||||
|
|
||||||
var pdvm = new ProductsDisplayViewModel();
|
var pdvm = new ProductsDisplayViewModel();
|
||||||
_ = pdvm.BindToGridAsync(sampleEntries);
|
_ = pdvm.BindToGridAsync(sampleEntries.OfType<LibraryBook>().ToList());
|
||||||
DataContext = pdvm;
|
DataContext = pdvm;
|
||||||
|
|
||||||
setGridScale(1);
|
setGridScale(1);
|
||||||
|
|||||||
@ -84,7 +84,7 @@ namespace LibationFileManager
|
|||||||
ProcessDirectory,
|
ProcessDirectory,
|
||||||
LocalAppData,
|
LocalAppData,
|
||||||
UserProfile,
|
UserProfile,
|
||||||
Path.Combine(Path.GetTempPath(), "Libation")
|
WinTemp,
|
||||||
};
|
};
|
||||||
|
|
||||||
//Try to find and validate appsettings.json in each folder
|
//Try to find and validate appsettings.json in each folder
|
||||||
@ -181,7 +181,7 @@ namespace LibationFileManager
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -83,9 +83,11 @@ namespace LibationWinForms.Dialogs
|
|||||||
if (lb.Book.UserDefinedItem.BookStatus is not LiberatedStatus.Liberated)
|
if (lb.Book.UserDefinedItem.BookStatus is not LiberatedStatus.Liberated)
|
||||||
await Task.Run(() => lb.UpdateBookStatus(LiberatedStatus.Liberated));
|
await Task.Run(() => lb.UpdateBookStatus(LiberatedStatus.Liberated));
|
||||||
|
|
||||||
|
tokenSource.Token.ThrowIfCancellationRequested();
|
||||||
this.Invoke(FileFound, this, book);
|
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);
|
Serilog.Log.Error(ex, "Error adding found audiobook file to Libation. {@audioFile}", book);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user