ToolBox: Add cancel

This commit is contained in:
Genteure 2021-05-02 21:34:27 +08:00
parent b931e24523
commit 8615e1421f
9 changed files with 96 additions and 31 deletions

View File

@ -4,6 +4,7 @@ using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.IO.Pipelines; using System.IO.Pipelines;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BililiveRecorder.Flv; using BililiveRecorder.Flv;
using BililiveRecorder.Flv.Amf; using BililiveRecorder.Flv.Amf;
@ -48,9 +49,9 @@ namespace BililiveRecorder.ToolBox.Commands
{ {
private static readonly ILogger logger = Log.ForContext<AnalyzeHandler>(); private static readonly ILogger logger = Log.ForContext<AnalyzeHandler>();
public Task<CommandResponse<AnalyzeResponse>> Handle(AnalyzeRequest request) => this.Handle(request, null); public Task<CommandResponse<AnalyzeResponse>> Handle(AnalyzeRequest request) => this.Handle(request, default, null);
public async Task<CommandResponse<AnalyzeResponse>> Handle(AnalyzeRequest request, Func<double, Task>? progress) public async Task<CommandResponse<AnalyzeResponse>> Handle(AnalyzeRequest request, CancellationToken cancellationToken, Func<double, Task>? progress)
{ {
FileStream? flvFileStream = null; FileStream? flvFileStream = null;
try try
@ -109,9 +110,9 @@ namespace BililiveRecorder.ToolBox.Commands
await Task.Run(async () => await Task.Run(async () =>
{ {
var count = 0; var count = 0;
while (true) while (!cancellationToken.IsCancellationRequested)
{ {
var group = await grouping.ReadGroupAsync(default).ConfigureAwait(false); var group = await grouping.ReadGroupAsync(cancellationToken).ConfigureAwait(false);
if (group is null) if (group is null)
break; break;
@ -136,6 +137,9 @@ namespace BililiveRecorder.ToolBox.Commands
} }
}).ConfigureAwait(false); }).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested)
return new CommandResponse<AnalyzeResponse> { Status = ResponseStatus.Cancelled };
// Result // Result
var response = await Task.Run(() => var response = await Task.Run(() =>
{ {
@ -169,6 +173,10 @@ namespace BililiveRecorder.ToolBox.Commands
Result = response Result = response
}; };
} }
catch (TaskCanceledException) when (cancellationToken.IsCancellationRequested)
{
return new CommandResponse<AnalyzeResponse> { Status = ResponseStatus.Cancelled };
}
catch (NotFlvFileException ex) catch (NotFlvFileException ex)
{ {
return new CommandResponse<AnalyzeResponse> return new CommandResponse<AnalyzeResponse>

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.IO.Pipelines; using System.IO.Pipelines;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BililiveRecorder.Flv; using BililiveRecorder.Flv;
using BililiveRecorder.Flv.Parser; using BililiveRecorder.Flv.Parser;
@ -26,9 +27,9 @@ namespace BililiveRecorder.ToolBox.Commands
{ {
private static readonly ILogger logger = Log.ForContext<ExportHandler>(); private static readonly ILogger logger = Log.ForContext<ExportHandler>();
public Task<CommandResponse<ExportResponse>> Handle(ExportRequest request) => this.Handle(request, null); public Task<CommandResponse<ExportResponse>> Handle(ExportRequest request) => this.Handle(request, default, null);
public async Task<CommandResponse<ExportResponse>> Handle(ExportRequest request, Func<double, Task>? progress) public async Task<CommandResponse<ExportResponse>> Handle(ExportRequest request, CancellationToken cancellationToken, Func<double, Task>? progress)
{ {
FileStream? inputStream = null, outputStream = null; FileStream? inputStream = null, outputStream = null;
try try
@ -67,9 +68,9 @@ namespace BililiveRecorder.ToolBox.Commands
var tags = new List<Tag>(); var tags = new List<Tag>();
var memoryStreamProvider = new RecyclableMemoryStreamProvider(); var memoryStreamProvider = new RecyclableMemoryStreamProvider();
using var reader = new FlvTagPipeReader(PipeReader.Create(inputStream), memoryStreamProvider, skipData: true, logger: logger); using var reader = new FlvTagPipeReader(PipeReader.Create(inputStream), memoryStreamProvider, skipData: true, logger: logger);
while (true) while (!cancellationToken.IsCancellationRequested)
{ {
var tag = await reader.ReadTagAsync(default).ConfigureAwait(false); var tag = await reader.ReadTagAsync(cancellationToken).ConfigureAwait(false);
if (tag is null) break; if (tag is null) break;
tags.Add(tag); tags.Add(tag);
@ -79,6 +80,9 @@ namespace BililiveRecorder.ToolBox.Commands
return tags; return tags;
}); });
if (cancellationToken.IsCancellationRequested)
return new CommandResponse<ExportResponse> { Status = ResponseStatus.Cancelled };
await Task.Run(() => await Task.Run(() =>
{ {
using var writer = new StreamWriter(new GZipStream(outputStream, CompressionLevel.Optimal)); using var writer = new StreamWriter(new GZipStream(outputStream, CompressionLevel.Optimal));
@ -90,6 +94,10 @@ namespace BililiveRecorder.ToolBox.Commands
return new CommandResponse<ExportResponse> { Status = ResponseStatus.OK, Result = new ExportResponse() }; return new CommandResponse<ExportResponse> { Status = ResponseStatus.OK, Result = new ExportResponse() };
} }
catch (TaskCanceledException) when (cancellationToken.IsCancellationRequested)
{
return new CommandResponse<ExportResponse> { Status = ResponseStatus.Cancelled };
}
catch (NotFlvFileException ex) catch (NotFlvFileException ex)
{ {
return new CommandResponse<ExportResponse> return new CommandResponse<ExportResponse>

View File

@ -4,6 +4,7 @@ using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.IO.Pipelines; using System.IO.Pipelines;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BililiveRecorder.Flv; using BililiveRecorder.Flv;
using BililiveRecorder.Flv.Grouping; using BililiveRecorder.Flv.Grouping;
@ -51,9 +52,9 @@ namespace BililiveRecorder.ToolBox.Commands
{ {
private static readonly ILogger logger = Log.ForContext<FixHandler>(); private static readonly ILogger logger = Log.ForContext<FixHandler>();
public Task<CommandResponse<FixResponse>> Handle(FixRequest request) => this.Handle(request, null); public Task<CommandResponse<FixResponse>> Handle(FixRequest request) => this.Handle(request, default, null);
public async Task<CommandResponse<FixResponse>> Handle(FixRequest request, Func<double, Task>? progress) public async Task<CommandResponse<FixResponse>> Handle(FixRequest request, CancellationToken cancellationToken, Func<double, Task>? progress)
{ {
FileStream? flvFileStream = null; FileStream? flvFileStream = null;
try try
@ -130,9 +131,9 @@ namespace BililiveRecorder.ToolBox.Commands
await Task.Run(async () => await Task.Run(async () =>
{ {
var count = 0; var count = 0;
while (true) while (!cancellationToken.IsCancellationRequested)
{ {
var group = await grouping.ReadGroupAsync(default).ConfigureAwait(false); var group = await grouping.ReadGroupAsync(cancellationToken).ConfigureAwait(false);
if (group is null) if (group is null)
break; break;
@ -157,6 +158,9 @@ namespace BililiveRecorder.ToolBox.Commands
} }
}).ConfigureAwait(false); }).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested)
return new CommandResponse<FixResponse> { Status = ResponseStatus.Cancelled };
// Post Run // Post Run
if (xmlMode) if (xmlMode)
{ {
@ -186,6 +190,9 @@ namespace BililiveRecorder.ToolBox.Commands
}); });
} }
if (cancellationToken.IsCancellationRequested)
return new CommandResponse<FixResponse> { Status = ResponseStatus.Cancelled };
// Result // Result
var response = await Task.Run(() => var response = await Task.Run(() =>
{ {
@ -215,6 +222,10 @@ namespace BililiveRecorder.ToolBox.Commands
return new CommandResponse<FixResponse> { Status = ResponseStatus.OK, Result = response }; return new CommandResponse<FixResponse> { Status = ResponseStatus.OK, Result = response };
} }
catch (TaskCanceledException) when (cancellationToken.IsCancellationRequested)
{
return new CommandResponse<FixResponse> { Status = ResponseStatus.Cancelled };
}
catch (NotFlvFileException ex) catch (NotFlvFileException ex)
{ {
return new CommandResponse<FixResponse> return new CommandResponse<FixResponse>

View File

@ -4,6 +4,7 @@ namespace BililiveRecorder.ToolBox
{ {
Error = 0, Error = 0,
OK, OK,
Cancelled,
NotFlvFile, NotFlvFile,
UnknownFlvTagType, UnknownFlvTagType,
InputIOError, InputIOError,

View File

@ -8,13 +8,15 @@
l:LocalizeDictionary.DesignCulture="" l:LocalizeDictionary.DesignCulture=""
l:ResxLocalizationProvider.DefaultAssembly="BililiveRecorder.WPF" l:ResxLocalizationProvider.DefaultAssembly="BililiveRecorder.WPF"
l:ResxLocalizationProvider.DefaultDictionary="Strings" l:ResxLocalizationProvider.DefaultDictionary="Strings"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:BililiveRecorder.WPF.Controls" xmlns:local="clr-namespace:BililiveRecorder.WPF.Controls"
Name="autoFixProgressDialog" Name="autoFixProgressDialog"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"> d:DesignHeight="450" d:DesignWidth="800">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<ui:ProgressRing Margin="10" Height="50" Width="50" IsActive="True"/> <ui:ProgressRing Margin="10" Height="50" Width="50" IsActive="True"/>
<ui:ProgressBar Value="{Binding ElementName=autoFixProgressDialog,Path=Progress}" Width="200"/> <ui:ProgressBar Value="{Binding ElementName=autoFixProgressDialog,Path=Progress}" Width="200"/>
<Button HorizontalAlignment="Center" Margin="10" Content="{l:Loc Global_Cancel}" Click="Button_Click"
Visibility="{Binding ElementName=autoFixProgressDialog,Path=CancelButtonVisibility}"/>
</StackPanel> </StackPanel>
</ui:ContentDialog> </ui:ContentDialog>

View File

@ -1,5 +1,7 @@
using System.Threading;
using System.Windows; using System.Windows;
#nullable enable
namespace BililiveRecorder.WPF.Controls namespace BililiveRecorder.WPF.Controls
{ {
/// <summary> /// <summary>
@ -20,5 +22,18 @@ namespace BililiveRecorder.WPF.Controls
get => (int)this.GetValue(ProgressProperty); get => (int)this.GetValue(ProgressProperty);
set => this.SetValue(ProgressProperty, value); set => this.SetValue(ProgressProperty, value);
} }
public static readonly DependencyProperty CancelButtonVisibilityProperty =
DependencyProperty.Register(nameof(CancelButtonVisibility), typeof(Visibility), typeof(AutoFixProgressDialog), new PropertyMetadata(Visibility.Collapsed));
public Visibility CancelButtonVisibility
{
get => (Visibility)this.GetValue(CancelButtonVisibilityProperty);
set => this.SetValue(CancelButtonVisibilityProperty, value);
}
public CancellationTokenSource? CancellationTokenSource { get; set; }
private void Button_Click(object sender, RoutedEventArgs e) => this.CancellationTokenSource?.Cancel();
} }
} }

View File

@ -88,7 +88,8 @@
<ui:PathIcon Style="{StaticResource PathIconDataToolboxOutline}"/> <ui:PathIcon Style="{StaticResource PathIconDataToolboxOutline}"/>
</ui:NavigationViewItem.Icon> </ui:NavigationViewItem.Icon>
<ui:NavigationViewItem.MenuItems> <ui:NavigationViewItem.MenuItems>
<ui:NavigationViewItem Content="{l:Loc Toolbox_AutoFix_Title}" Tag="ToolboxAutoFixPage"> <ui:NavigationViewItem l:ResxLocalizationProvider.DefaultDictionary="Strings"
Content="{l:Loc Toolbox_AutoFix_Title}" Tag="ToolboxAutoFixPage">
<ui:NavigationViewItem.Icon> <ui:NavigationViewItem.Icon>
<ui:PathIcon Style="{StaticResource PathIconDataAutoFix}"/> <ui:PathIcon Style="{StaticResource PathIconDataAutoFix}"/>
</ui:NavigationViewItem.Icon> </ui:NavigationViewItem.Icon>

View File

@ -27,7 +27,7 @@
<Button VerticalAlignment="Bottom" DockPanel.Dock="Right" Content="{l:Loc Toolbox_AutoFix_ButtonNotFixed}"> <Button VerticalAlignment="Bottom" DockPanel.Dock="Right" Content="{l:Loc Toolbox_AutoFix_ButtonNotFixed}">
<ui:FlyoutService.Flyout> <ui:FlyoutService.Flyout>
<ui:Flyout Placement="LeftEdgeAlignedTop"> <ui:Flyout Placement="LeftEdgeAlignedTop">
<StackPanel> <StackPanel l:ResxLocalizationProvider.DefaultDictionary="Strings">
<TextBlock Text="{l:Loc Toolbox_AutoFix_NotFixed_Description}"/> <TextBlock Text="{l:Loc Toolbox_AutoFix_NotFixed_Description}"/>
<ui:HyperlinkButton NavigateUri="" Content="{l:Loc Toolbox_AutoFix_NotFixed_LearnMore}"/> <ui:HyperlinkButton NavigateUri="" Content="{l:Loc Toolbox_AutoFix_NotFixed_LearnMore}"/>
<Button Margin="0,15,0,10" HorizontalAlignment="Center" Click="Export_Button_Click"> <Button Margin="0,15,0,10" HorizontalAlignment="Center" Click="Export_Button_Click">

View File

@ -1,5 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using BililiveRecorder.ToolBox; using BililiveRecorder.ToolBox;
@ -62,7 +63,12 @@ namespace BililiveRecorder.WPF.Pages
logger.Debug("修复文件 {Path}", inputPath); logger.Debug("修复文件 {Path}", inputPath);
progressDialog = new AutoFixProgressDialog(); progressDialog = new AutoFixProgressDialog()
{
CancelButtonVisibility = Visibility.Visible,
CancellationTokenSource = new CancellationTokenSource()
};
var token = progressDialog.CancellationTokenSource.Token;
var showTask = progressDialog.ShowAsync(); var showTask = progressDialog.ShowAsync();
string? output_path; string? output_path;
@ -94,7 +100,7 @@ namespace BililiveRecorder.WPF.Pages
var handler = new FixHandler(); var handler = new FixHandler();
var resp = await handler.Handle(req, async p => var resp = await handler.Handle(req, token, async p =>
{ {
await this.Dispatcher.InvokeAsync(() => await this.Dispatcher.InvokeAsync(() =>
{ {
@ -102,7 +108,7 @@ namespace BililiveRecorder.WPF.Pages
}); });
}).ConfigureAwait(true); }).ConfigureAwait(true);
if (resp.Status != ResponseStatus.OK) if (resp.Status != ResponseStatus.Cancelled && resp.Status != ResponseStatus.OK)
{ {
logger.Warning(resp.Exception, "修复时发生错误 (@Status)", resp.Status); logger.Warning(resp.Exception, "修复时发生错误 (@Status)", resp.Status);
await Task.Run(() => ShowErrorMessageBox(resp)).ConfigureAwait(true); await Task.Run(() => ShowErrorMessageBox(resp)).ConfigureAwait(true);
@ -145,7 +151,12 @@ namespace BililiveRecorder.WPF.Pages
logger.Debug("分析文件 {Path}", inputPath); logger.Debug("分析文件 {Path}", inputPath);
progressDialog = new AutoFixProgressDialog(); progressDialog = new AutoFixProgressDialog()
{
CancelButtonVisibility = Visibility.Visible,
CancellationTokenSource = new CancellationTokenSource()
};
var token = progressDialog.CancellationTokenSource.Token;
var showTask = progressDialog.ShowAsync(); var showTask = progressDialog.ShowAsync();
var req = new AnalyzeRequest var req = new AnalyzeRequest
@ -155,7 +166,7 @@ namespace BililiveRecorder.WPF.Pages
var handler = new AnalyzeHandler(); var handler = new AnalyzeHandler();
var resp = await handler.Handle(req, async p => var resp = await handler.Handle(req, token, async p =>
{ {
await this.Dispatcher.InvokeAsync(() => await this.Dispatcher.InvokeAsync(() =>
{ {
@ -163,14 +174,17 @@ namespace BililiveRecorder.WPF.Pages
}); });
}).ConfigureAwait(true); }).ConfigureAwait(true);
if (resp.Status != ResponseStatus.OK) if (resp.Status != ResponseStatus.Cancelled)
{ {
logger.Warning(resp.Exception, "分析时发生错误 (@Status)", resp.Status); if (resp.Status != ResponseStatus.OK)
await Task.Run(() => ShowErrorMessageBox(resp)).ConfigureAwait(true); {
} logger.Warning(resp.Exception, "分析时发生错误 (@Status)", resp.Status);
else await Task.Run(() => ShowErrorMessageBox(resp)).ConfigureAwait(true);
{ }
this.analyzeResultDisplayArea.DataContext = resp.Result; else
{
this.analyzeResultDisplayArea.DataContext = resp.Result;
}
} }
progressDialog.Hide(); progressDialog.Hide();
@ -203,7 +217,12 @@ namespace BililiveRecorder.WPF.Pages
logger.Debug("导出文件 {Path}", inputPath); logger.Debug("导出文件 {Path}", inputPath);
progressDialog = new AutoFixProgressDialog(); progressDialog = new AutoFixProgressDialog()
{
CancelButtonVisibility = Visibility.Visible,
CancellationTokenSource = new CancellationTokenSource()
};
var token = progressDialog.CancellationTokenSource.Token;
var showTask = progressDialog.ShowAsync(); var showTask = progressDialog.ShowAsync();
var outputPath = string.Empty; var outputPath = string.Empty;
@ -235,7 +254,7 @@ namespace BililiveRecorder.WPF.Pages
var handler = new ExportHandler(); var handler = new ExportHandler();
var resp = await handler.Handle(req, async p => var resp = await handler.Handle(req, token, async p =>
{ {
await this.Dispatcher.InvokeAsync(() => await this.Dispatcher.InvokeAsync(() =>
{ {
@ -243,7 +262,7 @@ namespace BililiveRecorder.WPF.Pages
}); });
}).ConfigureAwait(true); }).ConfigureAwait(true);
if (resp.Status != ResponseStatus.OK) if (resp.Status != ResponseStatus.Cancelled && resp.Status != ResponseStatus.OK)
{ {
logger.Warning(resp.Exception, "导出分析数据时发生错误 (@Status)", resp.Status); logger.Warning(resp.Exception, "导出分析数据时发生错误 (@Status)", resp.Status);
await Task.Run(() => ShowErrorMessageBox(resp)).ConfigureAwait(true); await Task.Run(() => ShowErrorMessageBox(resp)).ConfigureAwait(true);