mirror of
https://github.com/BililiveRecorder/BililiveRecorder.git
synced 2024-11-15 19:22:19 +08:00
Toolbox & CLI: Redo console output, Add configure
subcommand
This commit is contained in:
parent
e2d5a3fd47
commit
9c33d64734
202
BililiveRecorder.Cli/Configure/ConfigureCommand.cs
Normal file
202
BililiveRecorder.Cli/Configure/ConfigureCommand.cs
Normal file
|
@ -0,0 +1,202 @@
|
|||
using System;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BililiveRecorder.Core.Config;
|
||||
using BililiveRecorder.Core.Config.V2;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace BililiveRecorder.Cli.Configure
|
||||
{
|
||||
public class ConfigureCommand : Command
|
||||
{
|
||||
public ConfigureCommand() : base("configure", "Interactively configure config.json")
|
||||
{
|
||||
this.AddArgument(new Argument("path") { Description = "Path to work directory or config.json" });
|
||||
this.Handler = CommandHandler.Create<string>(Run);
|
||||
}
|
||||
|
||||
private static int Run(string path)
|
||||
{
|
||||
if (!FindConfig(path, out var config, out var fullPath))
|
||||
return 1;
|
||||
|
||||
ShowRootMenu(config, fullPath);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void ShowRootMenu(ConfigV2 config, string fullPath)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var selection = PromptEnumSelection<RootMenuSelection>();
|
||||
AnsiConsole.Clear();
|
||||
switch (selection)
|
||||
{
|
||||
case RootMenuSelection.ListRooms:
|
||||
ListRooms(config);
|
||||
break;
|
||||
case RootMenuSelection.AddRoom:
|
||||
AddRoom(config);
|
||||
break;
|
||||
case RootMenuSelection.DeleteRoom:
|
||||
DeleteRoom(config);
|
||||
break;
|
||||
case RootMenuSelection.SetRoomConfig:
|
||||
// TODO
|
||||
AnsiConsole.MarkupLine("[bold red]Not Implemented Yet[/]");
|
||||
break;
|
||||
case RootMenuSelection.SetGlobalConfig:
|
||||
// TODO
|
||||
AnsiConsole.MarkupLine("[bold red]Not Implemented Yet[/]");
|
||||
break;
|
||||
case RootMenuSelection.SetJsonSchema:
|
||||
SetJsonSchema(config);
|
||||
break;
|
||||
case RootMenuSelection.Exit:
|
||||
return;
|
||||
case RootMenuSelection.SaveAndExit:
|
||||
if (SaveConfig(config, fullPath))
|
||||
return;
|
||||
else
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ListRooms(ConfigV2 config)
|
||||
{
|
||||
var table = new Table()
|
||||
.AddColumns("Roomid", "AutoRecord")
|
||||
.Border(TableBorder.Rounded);
|
||||
|
||||
foreach (var room in config.Rooms)
|
||||
{
|
||||
table.AddRow(room.RoomId.ToString(), room.AutoRecord ? "[green]Enabled[/]" : "[red]Disabled[/]");
|
||||
}
|
||||
|
||||
AnsiConsole.Render(table);
|
||||
}
|
||||
|
||||
private static void AddRoom(ConfigV2 config)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var roomid = AnsiConsole.Prompt(new TextPrompt<int>("[grey](type 0 to cancel)[/] [green]Roomid[/]:").Validate(x => x switch
|
||||
{
|
||||
< 0 => ValidationResult.Error("[red]Roomid can't be negative[/]"),
|
||||
_ => ValidationResult.Success(),
|
||||
}));
|
||||
|
||||
if (roomid == 0)
|
||||
break;
|
||||
|
||||
if (config.Rooms.Any(x => x.RoomId == roomid))
|
||||
{
|
||||
AnsiConsole.MarkupLine("[red]Room already exist[/]");
|
||||
continue;
|
||||
}
|
||||
|
||||
var autoRecord = AnsiConsole.Confirm("Enable auto record?");
|
||||
|
||||
config.Rooms.Add(new RoomConfig { RoomId = roomid, AutoRecord = autoRecord });
|
||||
|
||||
AnsiConsole.MarkupLine("[green]Room {0} added to config[/]", roomid);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeleteRoom(ConfigV2 config)
|
||||
{
|
||||
var toBeDeleted = AnsiConsole.Prompt(new MultiSelectionPrompt<RoomConfig>()
|
||||
.Title("Delete rooms")
|
||||
.NotRequired()
|
||||
.UseConverter(r => r.RoomId.ToString())
|
||||
.PageSize(15)
|
||||
.MoreChoicesText("[grey](Move up and down to reveal more rooms)[/]")
|
||||
.InstructionsText("[grey](Press [blue]<space>[/] to toggle selection, [green]<enter>[/] to delete)[/]")
|
||||
.AddChoices(config.Rooms));
|
||||
|
||||
for (var i = 0; i < toBeDeleted.Count; i++)
|
||||
config.Rooms.Remove(toBeDeleted[i]);
|
||||
|
||||
AnsiConsole.MarkupLine("[green]{0} rooms deleted[/]", toBeDeleted.Count);
|
||||
}
|
||||
|
||||
private static void SetJsonSchema(ConfigV2 config)
|
||||
{
|
||||
var selection = PromptEnumSelection<JsonSchemaSelection>();
|
||||
switch (selection)
|
||||
{
|
||||
case JsonSchemaSelection.Default:
|
||||
config.DollarSignSchema = "https://raw.githubusercontent.com/Bililive/BililiveRecorder/dev-1.3/BililiveRecorder.Core/Config/V2/config.schema.json";
|
||||
break;
|
||||
case JsonSchemaSelection.Custom:
|
||||
config.DollarSignSchema = AnsiConsole.Prompt(new TextPrompt<string>("[green]JSON Schema[/]:").AllowEmpty());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool SaveConfig(ConfigV2 config, string fullPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = ConfigParser.SaveJson(config);
|
||||
using var file = new StreamWriter(File.Open(fullPath, FileMode.Create, FileAccess.ReadWrite, FileShare.None));
|
||||
file.Write(json);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AnsiConsole.MarkupLine("[red]Write config failed[/]");
|
||||
AnsiConsole.WriteException(ex, ExceptionFormats.ShortenPaths | ExceptionFormats.ShowLinks);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool FindConfig(string path, [NotNullWhen(true)] out ConfigV2? config, out string fullPath)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
fullPath = Path.GetFullPath(path);
|
||||
goto readFile;
|
||||
}
|
||||
else if (Directory.Exists(path))
|
||||
{
|
||||
fullPath = Path.GetFullPath(Path.Combine(path, ConfigParser.CONFIG_FILE_NAME));
|
||||
goto readFile;
|
||||
}
|
||||
else
|
||||
{
|
||||
AnsiConsole.MarkupLine("[red]Path does not exist.[/]");
|
||||
config = null;
|
||||
fullPath = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
readFile:
|
||||
config = ConfigParser.LoadJson(File.ReadAllText(fullPath, Encoding.UTF8));
|
||||
var result = config != null;
|
||||
if (!result)
|
||||
AnsiConsole.MarkupLine("[red]Load failed.\nBroken or corrupted file, or no permission.[/]");
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string EnumToDescriptionConverter<T>(T value) where T : struct, Enum
|
||||
{
|
||||
var type = typeof(T);
|
||||
var attrs = type.GetMember(Enum.GetName(type, value)!)[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
|
||||
return (attrs.Length > 0) ? ((DescriptionAttribute)attrs[0]).Description : string.Empty;
|
||||
}
|
||||
|
||||
private static T PromptEnumSelection<T>() where T : struct, Enum => AnsiConsole.Prompt(new SelectionPrompt<T>().AddChoices(Enum.GetValues<T>()).UseConverter(EnumToDescriptionConverter));
|
||||
}
|
||||
}
|
13
BililiveRecorder.Cli/Configure/JsonSchemaSelection.cs
Normal file
13
BililiveRecorder.Cli/Configure/JsonSchemaSelection.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System.ComponentModel;
|
||||
|
||||
namespace BililiveRecorder.Cli.Configure
|
||||
{
|
||||
public enum JsonSchemaSelection
|
||||
{
|
||||
[Description("https://raw.githubusercontent.com/.../config.schema.json")]
|
||||
Default,
|
||||
|
||||
[Description("Custom")]
|
||||
Custom
|
||||
}
|
||||
}
|
31
BililiveRecorder.Cli/Configure/RootMenuSelection.cs
Normal file
31
BililiveRecorder.Cli/Configure/RootMenuSelection.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using System.ComponentModel;
|
||||
|
||||
namespace BililiveRecorder.Cli.Configure
|
||||
{
|
||||
public enum RootMenuSelection
|
||||
{
|
||||
[Description("List rooms")]
|
||||
ListRooms,
|
||||
|
||||
[Description("Add room")]
|
||||
AddRoom,
|
||||
|
||||
[Description("Delete room")]
|
||||
DeleteRoom,
|
||||
|
||||
[Description("Update room config")]
|
||||
SetRoomConfig,
|
||||
|
||||
[Description("Update global config")]
|
||||
SetGlobalConfig,
|
||||
|
||||
[Description("Update JSON Schema")]
|
||||
SetJsonSchema,
|
||||
|
||||
[Description("Exit and discard all changes")]
|
||||
Exit,
|
||||
|
||||
[Description("Save and Exit")]
|
||||
SaveAndExit,
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ using System.CommandLine.Invocation;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using BililiveRecorder.Cli.Configure;
|
||||
using BililiveRecorder.Core;
|
||||
using BililiveRecorder.Core.Config;
|
||||
using BililiveRecorder.Core.Config.V2;
|
||||
|
@ -50,6 +51,7 @@ namespace BililiveRecorder.Cli
|
|||
{
|
||||
cmd_run,
|
||||
cmd_portable,
|
||||
new ConfigureCommand(),
|
||||
new ToolCommand()
|
||||
};
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace BililiveRecorder.Core.Config
|
|||
{
|
||||
public class ConfigParser
|
||||
{
|
||||
private const string CONFIG_FILE_NAME = "config.json";
|
||||
public const string CONFIG_FILE_NAME = "config.json";
|
||||
private static readonly ILogger logger = Log.ForContext<ConfigParser>();
|
||||
private static readonly JsonSerializerSettings settings = new JsonSerializerSettings()
|
||||
{
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
|
||||
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.1.2" />
|
||||
<PackageReference Include="Spectre.Console" Version="0.40.0" />
|
||||
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.21216.1" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -4,12 +4,12 @@ using Newtonsoft.Json.Converters;
|
|||
|
||||
namespace BililiveRecorder.ToolBox
|
||||
{
|
||||
public class CommandResponse<TResponse> where TResponse : class
|
||||
public class CommandResponse<TResponseData> where TResponseData : IResponseData
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public ResponseStatus Status { get; set; }
|
||||
|
||||
public TResponse? Result { get; set; }
|
||||
public TResponseData? Data { get; set; }
|
||||
|
||||
public string? ErrorMessage { get; set; }
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BililiveRecorder.ToolBox
|
||||
{
|
||||
public interface ICommandHandler<TRequest, TResponse>
|
||||
where TRequest : ICommandRequest<TResponse>
|
||||
where TResponse : class
|
||||
where TResponse : IResponseData
|
||||
{
|
||||
Task<CommandResponse<TResponse>> Handle(TRequest request);
|
||||
void PrintResponse(TResponse response);
|
||||
string Name { get; }
|
||||
Task<CommandResponse<TResponse>> Handle(TRequest request, CancellationToken cancellationToken, ProgressCallback? progress);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
namespace BililiveRecorder.ToolBox
|
||||
{
|
||||
public interface ICommandRequest<TResponse>
|
||||
where TResponse : class
|
||||
where TResponse : IResponseData
|
||||
{ }
|
||||
}
|
||||
|
|
7
BililiveRecorder.ToolBox/IResponseData.cs
Normal file
7
BililiveRecorder.ToolBox/IResponseData.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace BililiveRecorder.ToolBox
|
||||
{
|
||||
public interface IResponseData
|
||||
{
|
||||
void PrintToConsole();
|
||||
}
|
||||
}
|
6
BililiveRecorder.ToolBox/ProgressCallback.cs
Normal file
6
BililiveRecorder.ToolBox/ProgressCallback.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace BililiveRecorder.ToolBox
|
||||
{
|
||||
public delegate Task ProgressCallback(double progress);
|
||||
}
|
|
@ -24,9 +24,9 @@ namespace BililiveRecorder.ToolBox.Tool.Analyze
|
|||
{
|
||||
private static readonly ILogger logger = Log.ForContext<AnalyzeHandler>();
|
||||
|
||||
public Task<CommandResponse<AnalyzeResponse>> Handle(AnalyzeRequest request) => this.Handle(request, default, null);
|
||||
public string Name => "Analyze";
|
||||
|
||||
public async Task<CommandResponse<AnalyzeResponse>> Handle(AnalyzeRequest request, CancellationToken cancellationToken, Func<double, Task>? progress)
|
||||
public async Task<CommandResponse<AnalyzeResponse>> Handle(AnalyzeRequest request, CancellationToken cancellationToken, ProgressCallback? progress)
|
||||
{
|
||||
FileStream? flvFileStream = null;
|
||||
try
|
||||
|
@ -152,7 +152,7 @@ namespace BililiveRecorder.ToolBox.Tool.Analyze
|
|||
return new CommandResponse<AnalyzeResponse>
|
||||
{
|
||||
Status = ResponseStatus.OK,
|
||||
Result = response
|
||||
Data = response
|
||||
};
|
||||
}
|
||||
catch (TaskCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||
|
@ -192,31 +192,6 @@ namespace BililiveRecorder.ToolBox.Tool.Analyze
|
|||
}
|
||||
}
|
||||
|
||||
public void PrintResponse(AnalyzeResponse response)
|
||||
{
|
||||
Console.Write("Input: ");
|
||||
Console.WriteLine(response.InputPath);
|
||||
|
||||
Console.WriteLine(response.NeedFix ? "File needs repair" : "File doesn't need repair");
|
||||
|
||||
if (response.Unrepairable)
|
||||
Console.WriteLine("File contains error(s) that are unrepairable (yet), please send sample to the author of this program.");
|
||||
|
||||
Console.WriteLine("Will output {0} file(s) if repaired", response.OutputFileCount);
|
||||
|
||||
Console.WriteLine("Types of error:");
|
||||
Console.Write("Other: ");
|
||||
Console.WriteLine(response.IssueTypeOther);
|
||||
Console.Write("Unrepairable: ");
|
||||
Console.WriteLine(response.IssueTypeUnrepairable);
|
||||
Console.Write("TimestampJump: ");
|
||||
Console.WriteLine(response.IssueTypeTimestampJump);
|
||||
Console.Write("DecodingHeader: ");
|
||||
Console.WriteLine(response.IssueTypeDecodingHeader);
|
||||
Console.Write("RepeatingData: ");
|
||||
Console.WriteLine(response.IssueTypeRepeatingData);
|
||||
}
|
||||
|
||||
private class AnalyzeMockFlvTagWriter : IFlvTagWriter
|
||||
{
|
||||
public long FileSize => 0;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
using BililiveRecorder.ToolBox.ProcessingRules;
|
||||
using BililiveRecorder.ToolBox.ProcessingRules;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace BililiveRecorder.ToolBox.Tool.Analyze
|
||||
{
|
||||
public class AnalyzeResponse
|
||||
public class AnalyzeResponse : IResponseData
|
||||
{
|
||||
public string InputPath { get; set; } = string.Empty;
|
||||
|
||||
|
@ -20,5 +21,44 @@ namespace BililiveRecorder.ToolBox.Tool.Analyze
|
|||
public int IssueTypeTimestampOffset { get; set; }
|
||||
public int IssueTypeDecodingHeader { get; set; }
|
||||
public int IssueTypeRepeatingData { get; set; }
|
||||
|
||||
public void PrintToConsole()
|
||||
{
|
||||
if (this.NeedFix)
|
||||
AnsiConsole.Render(new FigletText("Need Fix").Color(Color.Red));
|
||||
else
|
||||
AnsiConsole.Render(new FigletText("All Good").Color(Color.Green));
|
||||
|
||||
if (this.Unrepairable)
|
||||
{
|
||||
AnsiConsole.Render(new Panel("This file contains error(s) that are identified as unrepairable (yet).\n" +
|
||||
"Please check if you're using the newest version.\n" +
|
||||
"Please consider send a sample file to the developer.")
|
||||
{
|
||||
Header = new PanelHeader("Important Note"),
|
||||
Border = BoxBorder.Rounded,
|
||||
BorderStyle = new Style(foreground: Color.Red)
|
||||
});
|
||||
}
|
||||
|
||||
AnsiConsole.Render(new Panel(this.InputPath.EscapeMarkup())
|
||||
{
|
||||
Header = new PanelHeader("Input"),
|
||||
Border = BoxBorder.Rounded
|
||||
});
|
||||
|
||||
AnsiConsole.MarkupLine("Will output [lime]{0}[/] file(s) if repaired", this.OutputFileCount);
|
||||
|
||||
AnsiConsole.Render(new Table()
|
||||
.Border(TableBorder.Rounded)
|
||||
.AddColumns("Category", "Count")
|
||||
.AddRow("Unrepairable", this.IssueTypeUnrepairable.ToString())
|
||||
.AddRow("Other", this.IssueTypeOther.ToString())
|
||||
.AddRow("TimestampJump", this.IssueTypeTimestampJump.ToString())
|
||||
.AddRow("TimestampOffset", this.IssueTypeTimestampOffset.ToString())
|
||||
.AddRow("DecodingHeader", this.IssueTypeDecodingHeader.ToString())
|
||||
.AddRow("RepeatingData", this.IssueTypeRepeatingData.ToString())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,10 +15,10 @@ namespace BililiveRecorder.ToolBox.Tool.Export
|
|||
public class ExportHandler : ICommandHandler<ExportRequest, ExportResponse>
|
||||
{
|
||||
private static readonly ILogger logger = Log.ForContext<ExportHandler>();
|
||||
|
||||
public string Name => "Export";
|
||||
|
||||
public Task<CommandResponse<ExportResponse>> Handle(ExportRequest request) => this.Handle(request, default, null);
|
||||
|
||||
public async Task<CommandResponse<ExportResponse>> Handle(ExportRequest request, CancellationToken cancellationToken, Func<double, Task>? progress)
|
||||
public async Task<CommandResponse<ExportResponse>> Handle(ExportRequest request, CancellationToken cancellationToken, ProgressCallback? progress)
|
||||
{
|
||||
FileStream? inputStream = null, outputStream = null;
|
||||
try
|
||||
|
@ -92,7 +92,7 @@ namespace BililiveRecorder.ToolBox.Tool.Export
|
|||
});
|
||||
});
|
||||
|
||||
return new CommandResponse<ExportResponse> { Status = ResponseStatus.OK, Result = new ExportResponse() };
|
||||
return new CommandResponse<ExportResponse> { Status = ResponseStatus.OK, Data = new ExportResponse() };
|
||||
}
|
||||
catch (TaskCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
|
@ -131,7 +131,5 @@ namespace BililiveRecorder.ToolBox.Tool.Export
|
|||
outputStream?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void PrintResponse(ExportResponse response) => Console.WriteLine("OK");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
namespace BililiveRecorder.ToolBox.Tool.Export
|
||||
namespace BililiveRecorder.ToolBox.Tool.Export
|
||||
{
|
||||
public class ExportResponse
|
||||
public class ExportResponse : IResponseData
|
||||
{
|
||||
public void PrintToConsole() { }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,9 +23,9 @@ namespace BililiveRecorder.ToolBox.Tool.Fix
|
|||
{
|
||||
private static readonly ILogger logger = Log.ForContext<FixHandler>();
|
||||
|
||||
public Task<CommandResponse<FixResponse>> Handle(FixRequest request) => this.Handle(request, default, null);
|
||||
public string Name => "Fix";
|
||||
|
||||
public async Task<CommandResponse<FixResponse>> Handle(FixRequest request, CancellationToken cancellationToken, Func<double, Task>? progress)
|
||||
public async Task<CommandResponse<FixResponse>> Handle(FixRequest request, CancellationToken cancellationToken, ProgressCallback? progress)
|
||||
{
|
||||
FileStream? flvFileStream = null;
|
||||
try
|
||||
|
@ -194,7 +194,7 @@ namespace BililiveRecorder.ToolBox.Tool.Fix
|
|||
};
|
||||
});
|
||||
|
||||
return new CommandResponse<FixResponse> { Status = ResponseStatus.OK, Result = response };
|
||||
return new CommandResponse<FixResponse> { Status = ResponseStatus.OK, Data = response };
|
||||
}
|
||||
catch (TaskCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
|
@ -233,37 +233,6 @@ namespace BililiveRecorder.ToolBox.Tool.Fix
|
|||
}
|
||||
}
|
||||
|
||||
public void PrintResponse(FixResponse response)
|
||||
{
|
||||
Console.Write("Input: ");
|
||||
Console.WriteLine(response.InputPath);
|
||||
|
||||
Console.WriteLine(response.NeedFix ? "File needs repair" : "File doesn't need repair");
|
||||
|
||||
if (response.Unrepairable)
|
||||
Console.WriteLine("File contains error(s) that are unrepairable (yet), please send sample to the author of this program.");
|
||||
|
||||
Console.WriteLine("{0} file(s) written", response.OutputFileCount);
|
||||
|
||||
foreach (var path in response.OutputPaths)
|
||||
{
|
||||
Console.Write(" ");
|
||||
Console.WriteLine(path);
|
||||
}
|
||||
|
||||
Console.WriteLine("Types of error:");
|
||||
Console.Write("Other: ");
|
||||
Console.WriteLine(response.IssueTypeOther);
|
||||
Console.Write("Unrepairable: ");
|
||||
Console.WriteLine(response.IssueTypeUnrepairable);
|
||||
Console.Write("TimestampJump: ");
|
||||
Console.WriteLine(response.IssueTypeTimestampJump);
|
||||
Console.Write("DecodingHeader: ");
|
||||
Console.WriteLine(response.IssueTypeDecodingHeader);
|
||||
Console.Write("RepeatingData: ");
|
||||
Console.WriteLine(response.IssueTypeRepeatingData);
|
||||
}
|
||||
|
||||
private class AutoFixFlvWriterTargetProvider : IFlvWriterTargetProvider
|
||||
{
|
||||
private readonly string pathTemplate;
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
using System;
|
||||
using System;
|
||||
using BililiveRecorder.ToolBox.ProcessingRules;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace BililiveRecorder.ToolBox.Tool.Fix
|
||||
{
|
||||
public class FixResponse
|
||||
public class FixResponse : IResponseData
|
||||
{
|
||||
public string InputPath { get; set; } = string.Empty;
|
||||
|
||||
|
@ -23,5 +24,48 @@ namespace BililiveRecorder.ToolBox.Tool.Fix
|
|||
public int IssueTypeTimestampOffset { get; set; }
|
||||
public int IssueTypeDecodingHeader { get; set; }
|
||||
public int IssueTypeRepeatingData { get; set; }
|
||||
|
||||
public void PrintToConsole()
|
||||
{
|
||||
AnsiConsole.Render(new FigletText("Done").Color(Color.Green));
|
||||
|
||||
if (this.Unrepairable)
|
||||
{
|
||||
AnsiConsole.Render(new Panel("This file contains error(s) that are identified as unrepairable (yet).\n" +
|
||||
"Please check if you're using the newest version.\n" +
|
||||
"Please consider send a sample file to the developer.")
|
||||
{
|
||||
Header = new PanelHeader("Important Note"),
|
||||
Border = BoxBorder.Rounded,
|
||||
BorderStyle = new Style(foreground: Color.Red)
|
||||
});
|
||||
}
|
||||
|
||||
AnsiConsole.Render(new Panel(this.InputPath.EscapeMarkup())
|
||||
{
|
||||
Header = new PanelHeader("Input"),
|
||||
Border = BoxBorder.Rounded
|
||||
});
|
||||
|
||||
var table_output = new Table()
|
||||
.Border(TableBorder.Rounded)
|
||||
.AddColumns("Output");
|
||||
|
||||
for (var i = 0; i < this.OutputPaths.Length; i++)
|
||||
table_output.AddRow(this.OutputPaths[i]);
|
||||
|
||||
AnsiConsole.Render(table_output);
|
||||
|
||||
AnsiConsole.Render(new Table()
|
||||
.Border(TableBorder.Rounded)
|
||||
.AddColumns("Category", "Count")
|
||||
.AddRow("Unrepairable", this.IssueTypeUnrepairable.ToString())
|
||||
.AddRow("Other", this.IssueTypeOther.ToString())
|
||||
.AddRow("TimestampJump", this.IssueTypeTimestampJump.ToString())
|
||||
.AddRow("TimestampOffset", this.IssueTypeTimestampOffset.ToString())
|
||||
.AddRow("DecodingHeader", this.IssueTypeDecodingHeader.ToString())
|
||||
.AddRow("RepeatingData", this.IssueTypeRepeatingData.ToString())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ using BililiveRecorder.ToolBox.Tool.Analyze;
|
|||
using BililiveRecorder.ToolBox.Tool.Export;
|
||||
using BililiveRecorder.ToolBox.Tool.Fix;
|
||||
using Newtonsoft.Json;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace BililiveRecorder.ToolBox
|
||||
{
|
||||
|
@ -34,7 +35,7 @@ namespace BililiveRecorder.ToolBox
|
|||
private void RegisterCommand<THandler, TRequest, TResponse>(string name, string? description, Action<Command> configure)
|
||||
where THandler : ICommandHandler<TRequest, TResponse>
|
||||
where TRequest : ICommandRequest<TResponse>
|
||||
where TResponse : class
|
||||
where TResponse : IResponseData
|
||||
{
|
||||
var cmd = new Command(name, description)
|
||||
{
|
||||
|
@ -49,32 +50,77 @@ namespace BililiveRecorder.ToolBox
|
|||
private static async Task<int> RunSubCommand<THandler, TRequest, TResponse>(TRequest request, bool json, bool jsonIndented)
|
||||
where THandler : ICommandHandler<TRequest, TResponse>
|
||||
where TRequest : ICommandRequest<TResponse>
|
||||
where TResponse : class
|
||||
where TResponse : IResponseData
|
||||
{
|
||||
var isInteractive = !(json || jsonIndented);
|
||||
var handler = Activator.CreateInstance<THandler>();
|
||||
|
||||
var response = await handler.Handle(request).ConfigureAwait(false);
|
||||
|
||||
if (json || jsonIndented)
|
||||
CommandResponse<TResponse>? response;
|
||||
if (isInteractive)
|
||||
{
|
||||
var json_str = JsonConvert.SerializeObject(response, jsonIndented ? Formatting.Indented : Formatting.None);
|
||||
Console.WriteLine(json_str);
|
||||
response = await AnsiConsole
|
||||
.Progress()
|
||||
.Columns(new ProgressColumn[]
|
||||
{
|
||||
new TaskDescriptionColumn(),
|
||||
new ProgressBarColumn(),
|
||||
new PercentageColumn(),
|
||||
new SpinnerColumn(Spinner.Known.Dots10),
|
||||
})
|
||||
.StartAsync(async ctx =>
|
||||
{
|
||||
var t = ctx.AddTask(handler.Name);
|
||||
t.MaxValue = 1d;
|
||||
var r = await handler.Handle(request, default, async p => t.Value = p).ConfigureAwait(false);
|
||||
t.Value = 1d;
|
||||
return r;
|
||||
})
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
response = await handler.Handle(request, default, null).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (isInteractive)
|
||||
{
|
||||
if (response.Status == ResponseStatus.OK)
|
||||
{
|
||||
handler.PrintResponse(response.Result!);
|
||||
response.Data?.PrintToConsole();
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Write("Error: ");
|
||||
Console.WriteLine(response.Status);
|
||||
Console.WriteLine(response.ErrorMessage);
|
||||
AnsiConsole.Render(new FigletText("Error").Color(Color.Red));
|
||||
|
||||
var errorInfo = new Table
|
||||
{
|
||||
Border = TableBorder.Rounded
|
||||
};
|
||||
errorInfo.AddColumn(new TableColumn("Error Code").Centered());
|
||||
errorInfo.AddColumn(new TableColumn("Error Message").Centered());
|
||||
errorInfo.AddRow("[red]" + response.Status.ToString().EscapeMarkup() + "[/]", "[red]" + (response.ErrorMessage ?? string.Empty) + "[/]");
|
||||
AnsiConsole.Render(errorInfo);
|
||||
|
||||
if (response.Exception is not null)
|
||||
AnsiConsole.Render(new Panel(response.Exception.GetRenderable(ExceptionFormats.ShortenPaths | ExceptionFormats.ShowLinks))
|
||||
{
|
||||
Header = new PanelHeader("Exception Info"),
|
||||
Border = BoxBorder.Rounded
|
||||
});
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var json_str = JsonConvert.SerializeObject(response, jsonIndented ? Formatting.Indented : Formatting.None);
|
||||
Console.WriteLine(json_str);
|
||||
|
||||
return 0;
|
||||
return response.Status == ResponseStatus.OK ? 0 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user