mirror of
https://github.com/BililiveRecorder/BililiveRecorder.git
synced 2024-11-16 03:32:20 +08:00
ToolBox: Better error handling
This commit is contained in:
parent
04cc54ee5c
commit
cf5dae1851
|
@ -138,10 +138,10 @@ namespace BililiveRecorder.Flv.Parser
|
||||||
fileHeaderSlice.CopyTo(stackSpan);
|
fileHeaderSlice.CopyTo(stackSpan);
|
||||||
|
|
||||||
if (data[0] != 'F' || data[1] != 'L' || data[2] != 'V' || data[3] != 1)
|
if (data[0] != 'F' || data[1] != 'L' || data[2] != 'V' || data[3] != 1)
|
||||||
throw new FlvException("Data is not FLV.");
|
throw new NotFlvFileException("Data is not FLV.");
|
||||||
|
|
||||||
if (data[5] != 0 || data[6] != 0 || data[7] != 0 || data[8] != 9)
|
if (data[5] != 0 || data[6] != 0 || data[7] != 0 || data[8] != 9)
|
||||||
throw new FlvException("Not Supported FLV format.");
|
throw new NotFlvFileException("Not Supported FLV format.");
|
||||||
|
|
||||||
buffer = buffer.Slice(fileHeaderSlice.End);
|
buffer = buffer.Slice(fileHeaderSlice.End);
|
||||||
|
|
||||||
|
@ -180,7 +180,7 @@ namespace BililiveRecorder.Flv.Parser
|
||||||
case TagType.Script:
|
case TagType.Script:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new FlvException("Unexpected Tag Type: " + header[0]);
|
throw new UnknownFlvTagTypeException("Unknown Tag Type: " + header[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read Tag Size
|
// Read Tag Size
|
||||||
|
|
16
BililiveRecorder.Flv/Parser/NotFlvFileException.cs
Normal file
16
BililiveRecorder.Flv/Parser/NotFlvFileException.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace BililiveRecorder.Flv.Parser
|
||||||
|
{
|
||||||
|
public class NotFlvFileException : FlvException
|
||||||
|
{
|
||||||
|
public NotFlvFileException() { }
|
||||||
|
|
||||||
|
public NotFlvFileException(string message) : base(message) { }
|
||||||
|
|
||||||
|
public NotFlvFileException(string message, Exception innerException) : base(message, innerException) { }
|
||||||
|
|
||||||
|
protected NotFlvFileException(SerializationInfo info, StreamingContext context) : base(info, context) { }
|
||||||
|
}
|
||||||
|
}
|
16
BililiveRecorder.Flv/Parser/UnknownFlvTagTypeException.cs
Normal file
16
BililiveRecorder.Flv/Parser/UnknownFlvTagTypeException.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace BililiveRecorder.Flv.Parser
|
||||||
|
{
|
||||||
|
public class UnknownFlvTagTypeException : FlvException
|
||||||
|
{
|
||||||
|
public UnknownFlvTagTypeException() { }
|
||||||
|
|
||||||
|
public UnknownFlvTagTypeException(string message) : base(message) { }
|
||||||
|
|
||||||
|
public UnknownFlvTagTypeException(string message, Exception innerException) : base(message, innerException) { }
|
||||||
|
|
||||||
|
protected UnknownFlvTagTypeException(SerializationInfo info, StreamingContext context) : base(info, context) { }
|
||||||
|
}
|
||||||
|
}
|
18
BililiveRecorder.ToolBox/CommandResponse.cs
Normal file
18
BililiveRecorder.ToolBox/CommandResponse.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
using System;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Converters;
|
||||||
|
|
||||||
|
namespace BililiveRecorder.ToolBox
|
||||||
|
{
|
||||||
|
public class CommandResponse<TResponse> where TResponse : class
|
||||||
|
{
|
||||||
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
|
public ResponseStatus Status { get; set; }
|
||||||
|
|
||||||
|
public TResponse? Result { get; set; }
|
||||||
|
|
||||||
|
public string? ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
public Exception? Exception { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
using System.IO.Pipelines;
|
using System.IO.Pipelines;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
@ -10,6 +11,7 @@ using BililiveRecorder.Flv.Grouping;
|
||||||
using BililiveRecorder.Flv.Parser;
|
using BililiveRecorder.Flv.Parser;
|
||||||
using BililiveRecorder.Flv.Pipeline;
|
using BililiveRecorder.Flv.Pipeline;
|
||||||
using BililiveRecorder.Flv.Writer;
|
using BililiveRecorder.Flv.Writer;
|
||||||
|
using BililiveRecorder.Flv.Xml;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
|
@ -40,22 +42,56 @@ namespace BililiveRecorder.ToolBox.Commands
|
||||||
{
|
{
|
||||||
private static readonly ILogger logger = Log.ForContext<AnalyzeHandler>();
|
private static readonly ILogger logger = Log.ForContext<AnalyzeHandler>();
|
||||||
|
|
||||||
public Task<AnalyzeResponse> Handle(AnalyzeRequest request) => this.Handle(request, null);
|
public Task<CommandResponse<AnalyzeResponse>> Handle(AnalyzeRequest request) => this.Handle(request, null);
|
||||||
|
|
||||||
public async Task<AnalyzeResponse> Handle(AnalyzeRequest request, Func<double, Task>? progress)
|
public async Task<CommandResponse<AnalyzeResponse>> Handle(AnalyzeRequest request, Func<double, Task>? progress)
|
||||||
{
|
{
|
||||||
var inputPath = Path.GetFullPath(request.Input);
|
try
|
||||||
|
|
||||||
var memoryStreamProvider = new DefaultMemoryStreamProvider();
|
|
||||||
var tagWriter = new AnalyzeMockFlvTagWriter();
|
|
||||||
var comments = new List<ProcessingComment>();
|
|
||||||
var context = new FlvProcessingContext();
|
|
||||||
var session = new Dictionary<object, object?>();
|
|
||||||
|
|
||||||
{
|
{
|
||||||
using var inputStream = File.OpenRead(inputPath);
|
var memoryStreamProvider = new DefaultMemoryStreamProvider();
|
||||||
|
var tagWriter = new AnalyzeMockFlvTagWriter();
|
||||||
|
var comments = new List<ProcessingComment>();
|
||||||
|
var context = new FlvProcessingContext();
|
||||||
|
var session = new Dictionary<object, object?>();
|
||||||
|
|
||||||
using var grouping = new TagGroupReader(new FlvTagPipeReader(PipeReader.Create(inputStream), memoryStreamProvider, skipData: false, logger: logger));
|
string? inputPath;
|
||||||
|
IFlvTagReader tagReader;
|
||||||
|
FileStream? flvFileStream = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
inputPath = Path.GetFullPath(request.Input);
|
||||||
|
if (inputPath.EndsWith(".gz", StringComparison.OrdinalIgnoreCase))
|
||||||
|
tagReader = await Task.Run(() =>
|
||||||
|
{
|
||||||
|
using var stream = new GZipStream(File.Open(inputPath, FileMode.Open, FileAccess.Read, FileShare.Read), CompressionMode.Decompress);
|
||||||
|
var xmlFlvFile = (XmlFlvFile)XmlFlvFile.Serializer.Deserialize(stream);
|
||||||
|
return new FlvTagListReader(xmlFlvFile.Tags);
|
||||||
|
});
|
||||||
|
else if (inputPath.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
|
||||||
|
tagReader = await Task.Run(() =>
|
||||||
|
{
|
||||||
|
using var stream = File.Open(inputPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
|
var xmlFlvFile = (XmlFlvFile)XmlFlvFile.Serializer.Deserialize(stream);
|
||||||
|
return new FlvTagListReader(xmlFlvFile.Tags);
|
||||||
|
});
|
||||||
|
else
|
||||||
|
{
|
||||||
|
flvFileStream = File.Open(inputPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
|
tagReader = new FlvTagPipeReader(PipeReader.Create(flvFileStream), memoryStreamProvider, skipData: false, logger: logger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex) when (ex is not FlvException)
|
||||||
|
{
|
||||||
|
return new CommandResponse<AnalyzeResponse>
|
||||||
|
{
|
||||||
|
Status = ResponseStatus.InputIOError,
|
||||||
|
Exception = ex,
|
||||||
|
ErrorMessage = ex.Message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
using var grouping = new TagGroupReader(tagReader);
|
||||||
using var writer = new FlvProcessingContextWriter(tagWriter: tagWriter, allowMissingHeader: true);
|
using var writer = new FlvProcessingContextWriter(tagWriter: tagWriter, allowMissingHeader: true);
|
||||||
var pipeline = new ProcessingPipelineBuilder(new ServiceCollection().BuildServiceProvider()).AddDefault().AddRemoveFillerData().Build();
|
var pipeline = new ProcessingPipelineBuilder(new ServiceCollection().BuildServiceProvider()).AddDefault().AddRemoveFillerData().Build();
|
||||||
|
|
||||||
|
@ -82,32 +118,63 @@ namespace BililiveRecorder.ToolBox.Commands
|
||||||
foreach (var tag in dataAction.Tags)
|
foreach (var tag in dataAction.Tags)
|
||||||
tag.BinaryData?.Dispose();
|
tag.BinaryData?.Dispose();
|
||||||
|
|
||||||
if (count++ % 10 == 0)
|
if (count++ % 10 == 0 && flvFileStream is not null && progress is not null)
|
||||||
{
|
await progress((double)flvFileStream.Position / flvFileStream.Length);
|
||||||
progress?.Invoke((double)inputStream.Position / inputStream.Length);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var response = await Task.Run(() =>
|
||||||
|
{
|
||||||
|
var countableComments = comments.Where(x => x.T != CommentType.Logging).ToArray();
|
||||||
|
return new AnalyzeResponse
|
||||||
|
{
|
||||||
|
InputPath = inputPath,
|
||||||
|
|
||||||
|
NeedFix = tagWriter.OutputFileCount != 1 || countableComments.Any(),
|
||||||
|
Unrepairable = countableComments.Any(x => x.T == CommentType.Unrepairable),
|
||||||
|
|
||||||
|
OutputFileCount = tagWriter.OutputFileCount,
|
||||||
|
|
||||||
|
IssueTypeOther = countableComments.Count(x => x.T == CommentType.Other),
|
||||||
|
IssueTypeUnrepairable = countableComments.Count(x => x.T == CommentType.Unrepairable),
|
||||||
|
IssueTypeTimestampJump = countableComments.Count(x => x.T == CommentType.TimestampJump),
|
||||||
|
IssueTypeDecodingHeader = countableComments.Count(x => x.T == CommentType.DecodingHeader),
|
||||||
|
IssueTypeRepeatingData = countableComments.Count(x => x.T == CommentType.RepeatingData)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return new CommandResponse<AnalyzeResponse>
|
||||||
|
{
|
||||||
|
Status = ResponseStatus.OK,
|
||||||
|
Result = response
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
catch (NotFlvFileException ex)
|
||||||
var countableComments = comments.Where(x => x.T != CommentType.Logging);
|
|
||||||
|
|
||||||
var response = new AnalyzeResponse
|
|
||||||
{
|
{
|
||||||
InputPath = inputPath,
|
return new CommandResponse<AnalyzeResponse>
|
||||||
|
{
|
||||||
NeedFix = tagWriter.OutputFileCount != 1 || countableComments.Any(),
|
Status = ResponseStatus.NotFlvFile,
|
||||||
Unrepairable = countableComments.Any(x => x.T == CommentType.Unrepairable),
|
Exception = ex,
|
||||||
|
ErrorMessage = ex.Message
|
||||||
OutputFileCount = tagWriter.OutputFileCount,
|
};
|
||||||
|
}
|
||||||
IssueTypeOther = countableComments.Count(x => x.T == CommentType.Other),
|
catch (UnknownFlvTagTypeException ex)
|
||||||
IssueTypeUnrepairable = countableComments.Count(x => x.T == CommentType.Unrepairable),
|
{
|
||||||
IssueTypeTimestampJump = countableComments.Count(x => x.T == CommentType.TimestampJump),
|
return new CommandResponse<AnalyzeResponse>
|
||||||
IssueTypeDecodingHeader = countableComments.Count(x => x.T == CommentType.DecodingHeader),
|
{
|
||||||
IssueTypeRepeatingData = countableComments.Count(x => x.T == CommentType.RepeatingData)
|
Status = ResponseStatus.UnknownFlvTagType,
|
||||||
};
|
Exception = ex,
|
||||||
|
ErrorMessage = ex.Message
|
||||||
return response;
|
};
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new CommandResponse<AnalyzeResponse>
|
||||||
|
{
|
||||||
|
Status = ResponseStatus.Error,
|
||||||
|
Exception = ex,
|
||||||
|
ErrorMessage = ex.Message
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PrintResponse(AnalyzeResponse response)
|
public void PrintResponse(AnalyzeResponse response)
|
||||||
|
|
|
@ -26,41 +26,103 @@ namespace BililiveRecorder.ToolBox.Commands
|
||||||
{
|
{
|
||||||
private static readonly ILogger logger = Log.ForContext<ExportHandler>();
|
private static readonly ILogger logger = Log.ForContext<ExportHandler>();
|
||||||
|
|
||||||
public Task<ExportResponse> Handle(ExportRequest request) => this.Handle(request, null);
|
public Task<CommandResponse<ExportResponse>> Handle(ExportRequest request) => this.Handle(request, null);
|
||||||
|
|
||||||
public async Task<ExportResponse> Handle(ExportRequest request, Func<double, Task>? progress)
|
public async Task<CommandResponse<ExportResponse>> Handle(ExportRequest request, Func<double, Task>? progress)
|
||||||
{
|
{
|
||||||
using var inputStream = File.OpenRead(request.Input);
|
FileStream? inputStream = null, outputStream = null;
|
||||||
using var outputStream = File.OpenWrite(request.Output);
|
try
|
||||||
|
|
||||||
var tags = new List<Tag>();
|
|
||||||
|
|
||||||
{
|
{
|
||||||
using var reader = new FlvTagPipeReader(PipeReader.Create(inputStream), new DefaultMemoryStreamProvider(), skipData: true, logger: logger);
|
try
|
||||||
var count = 0;
|
|
||||||
while (true)
|
|
||||||
{
|
{
|
||||||
var tag = await reader.ReadTagAsync(default).ConfigureAwait(false);
|
inputStream = File.Open(request.Input, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
if (tag is null) break;
|
|
||||||
tags.Add(tag);
|
|
||||||
|
|
||||||
if (count++ % 300 == 0)
|
|
||||||
progress?.Invoke((double)inputStream.Position / inputStream.Length);
|
|
||||||
}
|
}
|
||||||
}
|
catch (Exception ex)
|
||||||
|
|
||||||
{
|
|
||||||
using var writer = new StreamWriter(new GZipStream(outputStream, CompressionLevel.Optimal));
|
|
||||||
XmlFlvFile.Serializer.Serialize(writer, new XmlFlvFile
|
|
||||||
{
|
{
|
||||||
Tags = tags
|
return new CommandResponse<ExportResponse>
|
||||||
});
|
{
|
||||||
}
|
Status = ResponseStatus.InputIOError,
|
||||||
|
Exception = ex,
|
||||||
|
ErrorMessage = ex.Message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return new ExportResponse();
|
try
|
||||||
|
{
|
||||||
|
outputStream = File.OpenWrite(request.Output);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new CommandResponse<ExportResponse>
|
||||||
|
{
|
||||||
|
Status = ResponseStatus.OutputIOError,
|
||||||
|
Exception = ex,
|
||||||
|
ErrorMessage = ex.Message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var tags = await Task.Run(async () =>
|
||||||
|
{
|
||||||
|
var count = 0;
|
||||||
|
var tags = new List<Tag>();
|
||||||
|
using var reader = new FlvTagPipeReader(PipeReader.Create(inputStream), new DefaultMemoryStreamProvider(), skipData: true, logger: logger);
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var tag = await reader.ReadTagAsync(default).ConfigureAwait(false);
|
||||||
|
if (tag is null) break;
|
||||||
|
tags.Add(tag);
|
||||||
|
|
||||||
|
if (count++ % 300 == 0 && progress is not null)
|
||||||
|
await progress((double)inputStream.Position / inputStream.Length);
|
||||||
|
}
|
||||||
|
return tags;
|
||||||
|
});
|
||||||
|
|
||||||
|
await Task.Run(() =>
|
||||||
|
{
|
||||||
|
using var writer = new StreamWriter(new GZipStream(outputStream, CompressionLevel.Optimal));
|
||||||
|
XmlFlvFile.Serializer.Serialize(writer, new XmlFlvFile
|
||||||
|
{
|
||||||
|
Tags = tags
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return new CommandResponse<ExportResponse> { Status = ResponseStatus.OK, Result = new ExportResponse() };
|
||||||
|
}
|
||||||
|
catch (NotFlvFileException ex)
|
||||||
|
{
|
||||||
|
return new CommandResponse<ExportResponse>
|
||||||
|
{
|
||||||
|
Status = ResponseStatus.NotFlvFile,
|
||||||
|
Exception = ex,
|
||||||
|
ErrorMessage = ex.Message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (UnknownFlvTagTypeException ex)
|
||||||
|
{
|
||||||
|
return new CommandResponse<ExportResponse>
|
||||||
|
{
|
||||||
|
Status = ResponseStatus.UnknownFlvTagType,
|
||||||
|
Exception = ex,
|
||||||
|
ErrorMessage = ex.Message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new CommandResponse<ExportResponse>
|
||||||
|
{
|
||||||
|
Status = ResponseStatus.Error,
|
||||||
|
Exception = ex,
|
||||||
|
ErrorMessage = ex.Message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
inputStream?.Dispose();
|
||||||
|
outputStream?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PrintResponse(ExportResponse response)
|
public void PrintResponse(ExportResponse response) => Console.WriteLine("OK");
|
||||||
{ }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,79 +43,126 @@ namespace BililiveRecorder.ToolBox.Commands
|
||||||
{
|
{
|
||||||
private static readonly ILogger logger = Log.ForContext<FixHandler>();
|
private static readonly ILogger logger = Log.ForContext<FixHandler>();
|
||||||
|
|
||||||
public Task<FixResponse> Handle(FixRequest request) => this.Handle(request, null);
|
public Task<CommandResponse<FixResponse>> Handle(FixRequest request) => this.Handle(request, null);
|
||||||
|
|
||||||
public async Task<FixResponse> Handle(FixRequest request, Func<double, Task>? progress)
|
public async Task<CommandResponse<FixResponse>> Handle(FixRequest request, Func<double, Task>? progress)
|
||||||
{
|
{
|
||||||
var inputPath = Path.GetFullPath(request.Input);
|
FileStream? inputStream = null;
|
||||||
|
try
|
||||||
var outputPaths = new List<string>();
|
|
||||||
var targetProvider = new AutoFixFlvWriterTargetProvider(request.OutputBase);
|
|
||||||
targetProvider.BeforeFileOpen += (sender, path) => outputPaths.Add(path);
|
|
||||||
|
|
||||||
var memoryStreamProvider = new DefaultMemoryStreamProvider();
|
|
||||||
var tagWriter = new FlvTagFileWriter(targetProvider, memoryStreamProvider, logger);
|
|
||||||
|
|
||||||
var comments = new List<ProcessingComment>();
|
|
||||||
var context = new FlvProcessingContext();
|
|
||||||
var session = new Dictionary<object, object?>();
|
|
||||||
|
|
||||||
{
|
{
|
||||||
using var inputStream = File.OpenRead(inputPath);
|
var inputPath = Path.GetFullPath(request.Input);
|
||||||
|
|
||||||
using var grouping = new TagGroupReader(new FlvTagPipeReader(PipeReader.Create(inputStream), memoryStreamProvider, skipData: false, logger: logger));
|
var outputPaths = new List<string>();
|
||||||
using var writer = new FlvProcessingContextWriter(tagWriter: tagWriter, allowMissingHeader: true);
|
var targetProvider = new AutoFixFlvWriterTargetProvider(request.OutputBase);
|
||||||
var pipeline = new ProcessingPipelineBuilder(new ServiceCollection().BuildServiceProvider()).AddDefault().AddRemoveFillerData().Build();
|
targetProvider.BeforeFileOpen += (sender, path) => outputPaths.Add(path);
|
||||||
|
|
||||||
|
var memoryStreamProvider = new DefaultMemoryStreamProvider();
|
||||||
|
var tagWriter = new FlvTagFileWriter(targetProvider, memoryStreamProvider, logger);
|
||||||
|
|
||||||
|
var comments = new List<ProcessingComment>();
|
||||||
|
var context = new FlvProcessingContext();
|
||||||
|
var session = new Dictionary<object, object?>();
|
||||||
|
|
||||||
var count = 0;
|
|
||||||
while (true)
|
|
||||||
{
|
{
|
||||||
var group = await grouping.ReadGroupAsync(default).ConfigureAwait(false);
|
try
|
||||||
if (group is null)
|
|
||||||
break;
|
|
||||||
|
|
||||||
context.Reset(group, session);
|
|
||||||
pipeline(context);
|
|
||||||
|
|
||||||
if (context.Comments.Count > 0)
|
|
||||||
{
|
{
|
||||||
comments.AddRange(context.Comments);
|
inputStream = File.Open(inputPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
logger.Debug("修复逻辑输出 {@Comments}", context.Comments);
|
}
|
||||||
|
catch (Exception ex) when (ex is not FlvException)
|
||||||
|
{
|
||||||
|
return new CommandResponse<FixResponse>
|
||||||
|
{
|
||||||
|
Status = ResponseStatus.InputIOError,
|
||||||
|
Exception = ex,
|
||||||
|
ErrorMessage = ex.Message
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
await writer.WriteAsync(context).ConfigureAwait(false);
|
using var grouping = new TagGroupReader(new FlvTagPipeReader(PipeReader.Create(inputStream), memoryStreamProvider, skipData: false, logger: logger));
|
||||||
|
using var writer = new FlvProcessingContextWriter(tagWriter: tagWriter, allowMissingHeader: true);
|
||||||
|
var pipeline = new ProcessingPipelineBuilder(new ServiceCollection().BuildServiceProvider()).AddDefault().AddRemoveFillerData().Build();
|
||||||
|
|
||||||
foreach (var action in context.Actions)
|
var count = 0;
|
||||||
if (action is PipelineDataAction dataAction)
|
while (true)
|
||||||
foreach (var tag in dataAction.Tags)
|
|
||||||
tag.BinaryData?.Dispose();
|
|
||||||
|
|
||||||
if (count++ % 10 == 0)
|
|
||||||
{
|
{
|
||||||
progress?.Invoke((double)inputStream.Position / inputStream.Length);
|
var group = await grouping.ReadGroupAsync(default).ConfigureAwait(false);
|
||||||
|
if (group is null)
|
||||||
|
break;
|
||||||
|
|
||||||
|
context.Reset(group, session);
|
||||||
|
pipeline(context);
|
||||||
|
|
||||||
|
if (context.Comments.Count > 0)
|
||||||
|
{
|
||||||
|
comments.AddRange(context.Comments);
|
||||||
|
logger.Debug("修复逻辑输出 {@Comments}", context.Comments);
|
||||||
|
}
|
||||||
|
|
||||||
|
await writer.WriteAsync(context).ConfigureAwait(false);
|
||||||
|
|
||||||
|
foreach (var action in context.Actions)
|
||||||
|
if (action is PipelineDataAction dataAction)
|
||||||
|
foreach (var tag in dataAction.Tags)
|
||||||
|
tag.BinaryData?.Dispose();
|
||||||
|
|
||||||
|
if (count++ % 10 == 0 && progress is not null)
|
||||||
|
await progress((double)inputStream.Position / inputStream.Length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var response = await Task.Run(() =>
|
||||||
|
{
|
||||||
|
var countableComments = comments.Where(x => x.T != CommentType.Logging).ToArray();
|
||||||
|
return new FixResponse
|
||||||
|
{
|
||||||
|
InputPath = inputPath,
|
||||||
|
OutputPaths = outputPaths.ToArray(),
|
||||||
|
OutputFileCount = outputPaths.Count,
|
||||||
|
|
||||||
|
NeedFix = outputPaths.Count != 1 || countableComments.Any(),
|
||||||
|
Unrepairable = countableComments.Any(x => x.T == CommentType.Unrepairable),
|
||||||
|
|
||||||
|
IssueTypeOther = countableComments.Count(x => x.T == CommentType.Other),
|
||||||
|
IssueTypeUnrepairable = countableComments.Count(x => x.T == CommentType.Unrepairable),
|
||||||
|
IssueTypeTimestampJump = countableComments.Count(x => x.T == CommentType.TimestampJump),
|
||||||
|
IssueTypeDecodingHeader = countableComments.Count(x => x.T == CommentType.DecodingHeader),
|
||||||
|
IssueTypeRepeatingData = countableComments.Count(x => x.T == CommentType.RepeatingData)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return new CommandResponse<FixResponse> { Status = ResponseStatus.OK, Result = response };
|
||||||
}
|
}
|
||||||
|
catch (NotFlvFileException ex)
|
||||||
var countableComments = comments.Where(x => x.T != CommentType.Logging);
|
|
||||||
|
|
||||||
var response = new FixResponse
|
|
||||||
{
|
{
|
||||||
InputPath = inputPath,
|
return new CommandResponse<FixResponse>
|
||||||
OutputPaths = outputPaths.ToArray(),
|
{
|
||||||
OutputFileCount = outputPaths.Count,
|
Status = ResponseStatus.NotFlvFile,
|
||||||
|
Exception = ex,
|
||||||
NeedFix = outputPaths.Count != 1 || countableComments.Any(),
|
ErrorMessage = ex.Message
|
||||||
Unrepairable = countableComments.Any(x => x.T == CommentType.Unrepairable),
|
};
|
||||||
|
}
|
||||||
IssueTypeOther = countableComments.Count(x => x.T == CommentType.Other),
|
catch (UnknownFlvTagTypeException ex)
|
||||||
IssueTypeUnrepairable = countableComments.Count(x => x.T == CommentType.Unrepairable),
|
{
|
||||||
IssueTypeTimestampJump = countableComments.Count(x => x.T == CommentType.TimestampJump),
|
return new CommandResponse<FixResponse>
|
||||||
IssueTypeDecodingHeader = countableComments.Count(x => x.T == CommentType.DecodingHeader),
|
{
|
||||||
IssueTypeRepeatingData = countableComments.Count(x => x.T == CommentType.RepeatingData)
|
Status = ResponseStatus.UnknownFlvTagType,
|
||||||
};
|
Exception = ex,
|
||||||
|
ErrorMessage = ex.Message
|
||||||
return response;
|
};
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new CommandResponse<FixResponse>
|
||||||
|
{
|
||||||
|
Status = ResponseStatus.Error,
|
||||||
|
Exception = ex,
|
||||||
|
ErrorMessage = ex.Message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
inputStream?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PrintResponse(FixResponse response)
|
public void PrintResponse(FixResponse response)
|
||||||
|
|
|
@ -2,9 +2,11 @@ using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace BililiveRecorder.ToolBox
|
namespace BililiveRecorder.ToolBox
|
||||||
{
|
{
|
||||||
public interface ICommandHandler<TRequest, TResponse> where TRequest : ICommandRequest<TResponse>
|
public interface ICommandHandler<TRequest, TResponse>
|
||||||
|
where TRequest : ICommandRequest<TResponse>
|
||||||
|
where TResponse : class
|
||||||
{
|
{
|
||||||
Task<TResponse> Handle(TRequest request);
|
Task<CommandResponse<TResponse>> Handle(TRequest request);
|
||||||
void PrintResponse(TResponse response);
|
void PrintResponse(TResponse response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
namespace BililiveRecorder.ToolBox
|
namespace BililiveRecorder.ToolBox
|
||||||
{
|
{
|
||||||
public interface ICommandRequest<TResponse> { }
|
public interface ICommandRequest<TResponse>
|
||||||
|
where TResponse : class
|
||||||
|
{ }
|
||||||
}
|
}
|
||||||
|
|
12
BililiveRecorder.ToolBox/ResponseStatus.cs
Normal file
12
BililiveRecorder.ToolBox/ResponseStatus.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
namespace BililiveRecorder.ToolBox
|
||||||
|
{
|
||||||
|
public enum ResponseStatus
|
||||||
|
{
|
||||||
|
Error = 0,
|
||||||
|
OK,
|
||||||
|
NotFlvFile,
|
||||||
|
UnknownFlvTagType,
|
||||||
|
InputIOError,
|
||||||
|
OutputIOError,
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,25 +29,27 @@ namespace BililiveRecorder.ToolBox
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RegisterCommand<IHandler, IRequest, IResponse>(string name, string? description, Action<Command> configure)
|
private void RegisterCommand<THandler, TRequest, TResponse>(string name, string? description, Action<Command> configure)
|
||||||
where IHandler : ICommandHandler<IRequest, IResponse>
|
where THandler : ICommandHandler<TRequest, TResponse>
|
||||||
where IRequest : ICommandRequest<IResponse>
|
where TRequest : ICommandRequest<TResponse>
|
||||||
|
where TResponse : class
|
||||||
{
|
{
|
||||||
var cmd = new Command(name, description)
|
var cmd = new Command(name, description)
|
||||||
{
|
{
|
||||||
new Option<bool>("--json", "print result as json string"),
|
new Option<bool>("--json", "print result as json string"),
|
||||||
new Option<bool>("--json-indented", "print result as indented json string")
|
new Option<bool>("--json-indented", "print result as indented json string")
|
||||||
};
|
};
|
||||||
cmd.Handler = CommandHandler.Create((IRequest r, bool json, bool jsonIndented) => RunSubCommand<IHandler, IRequest, IResponse>(r, json, jsonIndented));
|
cmd.Handler = CommandHandler.Create((TRequest r, bool json, bool jsonIndented) => RunSubCommand<THandler, TRequest, TResponse>(r, json, jsonIndented));
|
||||||
configure(cmd);
|
configure(cmd);
|
||||||
this.Add(cmd);
|
this.Add(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<int> RunSubCommand<IHandler, IRequest, IResponse>(IRequest request, bool json, bool jsonIndented)
|
private static async Task<int> RunSubCommand<THandler, TRequest, TResponse>(TRequest request, bool json, bool jsonIndented)
|
||||||
where IHandler : ICommandHandler<IRequest, IResponse>
|
where THandler : ICommandHandler<TRequest, TResponse>
|
||||||
where IRequest : ICommandRequest<IResponse>
|
where TRequest : ICommandRequest<TResponse>
|
||||||
|
where TResponse : class
|
||||||
{
|
{
|
||||||
var handler = Activator.CreateInstance<IHandler>();
|
var handler = Activator.CreateInstance<THandler>();
|
||||||
|
|
||||||
var response = await handler.Handle(request).ConfigureAwait(false);
|
var response = await handler.Handle(request).ConfigureAwait(false);
|
||||||
|
|
||||||
|
@ -58,7 +60,16 @@ namespace BililiveRecorder.ToolBox
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
handler.PrintResponse(response);
|
if (response.Status == ResponseStatus.OK)
|
||||||
|
{
|
||||||
|
handler.PrintResponse(response.Result!);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.Write("Error: ");
|
||||||
|
Console.WriteLine(response.Status);
|
||||||
|
Console.WriteLine(response.ErrorMessage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -1,21 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
|
||||||
using System.IO.Pipelines;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using BililiveRecorder.Flv;
|
using BililiveRecorder.ToolBox;
|
||||||
using BililiveRecorder.Flv.Grouping;
|
|
||||||
using BililiveRecorder.Flv.Parser;
|
|
||||||
using BililiveRecorder.Flv.Pipeline;
|
|
||||||
using BililiveRecorder.Flv.Writer;
|
|
||||||
using BililiveRecorder.Flv.Xml;
|
|
||||||
using BililiveRecorder.ToolBox.Commands;
|
using BililiveRecorder.ToolBox.Commands;
|
||||||
using BililiveRecorder.WPF.Controls;
|
using BililiveRecorder.WPF.Controls;
|
||||||
using BililiveRecorder.WPF.Models;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.WindowsAPICodePack.Dialogs;
|
using Microsoft.WindowsAPICodePack.Dialogs;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
|
@ -50,7 +39,8 @@ namespace BililiveRecorder.WPF.Pages
|
||||||
NavigateToShortcut = true,
|
NavigateToShortcut = true,
|
||||||
Filters =
|
Filters =
|
||||||
{
|
{
|
||||||
new CommonFileDialogFilter("Flv files",".flv")
|
new CommonFileDialogFilter("Flv files",".flv"),
|
||||||
|
new CommonFileDialogFilter("Flv Xml files",".xml,.gz")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (fileDialog.ShowDialog() == CommonFileDialogResult.Ok)
|
if (fileDialog.ShowDialog() == CommonFileDialogResult.Ok)
|
||||||
|
@ -109,13 +99,25 @@ namespace BililiveRecorder.WPF.Pages
|
||||||
});
|
});
|
||||||
}).ConfigureAwait(true);
|
}).ConfigureAwait(true);
|
||||||
|
|
||||||
|
if (resp.Status != ResponseStatus.OK)
|
||||||
|
{
|
||||||
|
logger.Warning(resp.Exception, "修复时发生错误 (@Status)", resp.Status);
|
||||||
|
await Task.Run(() =>
|
||||||
|
{
|
||||||
|
// TODO 翻译
|
||||||
|
// 例:在读取文件时发生了错误
|
||||||
|
// 选择的不是 FLV 文件
|
||||||
|
// FLV 文件格式错误
|
||||||
|
MessageBox.Show($"错误类型: {resp.Status}\n{resp.ErrorMessage}", "修复时发生错误", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
}).ConfigureAwait(true);
|
||||||
|
}
|
||||||
|
|
||||||
progressDialog.Hide();
|
progressDialog.Hide();
|
||||||
await showTask.ConfigureAwait(true);
|
await showTask.ConfigureAwait(true);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
logger.Error(ex, "修复时发生错误");
|
logger.Error(ex, "修复时发生未处理的错误");
|
||||||
MessageBox.Show("修复时发生错误\n" + ex.Message);
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
@ -156,15 +158,29 @@ namespace BililiveRecorder.WPF.Pages
|
||||||
});
|
});
|
||||||
}).ConfigureAwait(true);
|
}).ConfigureAwait(true);
|
||||||
|
|
||||||
this.analyzeResultDisplayArea.DataContext = resp;
|
if (resp.Status != ResponseStatus.OK)
|
||||||
|
{
|
||||||
|
logger.Warning(resp.Exception, "分析时发生错误 (@Status)", resp.Status);
|
||||||
|
await Task.Run(() =>
|
||||||
|
{
|
||||||
|
// TODO 翻译
|
||||||
|
// 例:在读取文件时发生了错误
|
||||||
|
// 选择的不是 FLV 文件
|
||||||
|
// FLV 文件格式错误
|
||||||
|
MessageBox.Show($"错误类型: {resp.Status}\n{resp.ErrorMessage}", "分析时发生错误", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
}).ConfigureAwait(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.analyzeResultDisplayArea.DataContext = resp.Result;
|
||||||
|
}
|
||||||
|
|
||||||
progressDialog.Hide();
|
progressDialog.Hide();
|
||||||
await showTask.ConfigureAwait(true);
|
await showTask.ConfigureAwait(true);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
logger.Error(ex, "分析时发生错误");
|
logger.Error(ex, "分析时发生未处理的错误");
|
||||||
MessageBox.Show("分析时发生错误\n" + ex.Message);
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
@ -230,13 +246,25 @@ namespace BililiveRecorder.WPF.Pages
|
||||||
});
|
});
|
||||||
}).ConfigureAwait(true);
|
}).ConfigureAwait(true);
|
||||||
|
|
||||||
|
if (resp.Status != ResponseStatus.OK)
|
||||||
|
{
|
||||||
|
logger.Warning(resp.Exception, "导出分析数据时发生错误 (@Status)", resp.Status);
|
||||||
|
await Task.Run(() =>
|
||||||
|
{
|
||||||
|
// TODO 翻译
|
||||||
|
// 例:在读取文件时发生了错误
|
||||||
|
// 选择的不是 FLV 文件
|
||||||
|
// FLV 文件格式错误
|
||||||
|
MessageBox.Show($"错误类型: {resp.Status}\n{resp.ErrorMessage}", "导出分析数据时发生错误", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
}).ConfigureAwait(true);
|
||||||
|
}
|
||||||
|
|
||||||
progressDialog.Hide();
|
progressDialog.Hide();
|
||||||
await showTask.ConfigureAwait(true);
|
await showTask.ConfigureAwait(true);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
logger.Error(ex, "导出时发生错误");
|
logger.Error(ex, "导出时发生未处理的错误");
|
||||||
MessageBox.Show("导出时发生错误\n" + ex.Message);
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue
Block a user