New config gen

This commit is contained in:
Genteure 2021-08-13 21:03:21 +08:00
parent 405679a5d8
commit 5f96eb5393
17 changed files with 1218 additions and 361 deletions

View File

@ -1,6 +1,6 @@
// ******************************
// GENERATED CODE, DO NOT EDIT MANUALLY.
// SEE .tools/build_config.js
// SEE /config_gen/README.md
// ******************************
using System.Collections.Generic;
@ -13,18 +13,6 @@ namespace BililiveRecorder.Cli.Configure
{
[Description("[grey]Exit[/]")]
Exit,
TimingStreamRetry,
TimingStreamConnect,
TimingDanmakuRetry,
TimingCheckInterval,
TimingWatchdogTimeout,
RecordDanmakuFlushInterval,
Cookie,
WebHookUrls,
WebHookUrlsV2,
LiveApiHost,
RecordFilenameFormat,
WpfShowTitleAndArea,
RecordMode,
CuttingMode,
CuttingNumber,
@ -33,7 +21,19 @@ namespace BililiveRecorder.Cli.Configure
RecordDanmakuSuperChat,
RecordDanmakuGift,
RecordDanmakuGuard,
RecordingQuality
RecordingQuality,
RecordFilenameFormat,
WebHookUrls,
WebHookUrlsV2,
WpfShowTitleAndArea,
Cookie,
LiveApiHost,
TimingCheckInterval,
TimingStreamRetry,
TimingStreamConnect,
TimingDanmakuRetry,
TimingWatchdogTimeout,
RecordDanmakuFlushInterval
}
public enum RoomConfigProperties
{
@ -58,18 +58,6 @@ namespace BililiveRecorder.Cli.Configure
static ConfigInstructions()
{
GlobalConfig.Add(GlobalConfigProperties.TimingStreamRetry, new ConfigInstruction<GlobalConfig, uint>(config => config.HasTimingStreamRetry = false, (config, value) => config.TimingStreamRetry = value) { Name = "TimingStreamRetry", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.TimingStreamConnect, new ConfigInstruction<GlobalConfig, uint>(config => config.HasTimingStreamConnect = false, (config, value) => config.TimingStreamConnect = value) { Name = "TimingStreamConnect", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.TimingDanmakuRetry, new ConfigInstruction<GlobalConfig, uint>(config => config.HasTimingDanmakuRetry = false, (config, value) => config.TimingDanmakuRetry = value) { Name = "TimingDanmakuRetry", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.TimingCheckInterval, new ConfigInstruction<GlobalConfig, uint>(config => config.HasTimingCheckInterval = false, (config, value) => config.TimingCheckInterval = value) { Name = "TimingCheckInterval", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.TimingWatchdogTimeout, new ConfigInstruction<GlobalConfig, uint>(config => config.HasTimingWatchdogTimeout = false, (config, value) => config.TimingWatchdogTimeout = value) { Name = "TimingWatchdogTimeout", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.RecordDanmakuFlushInterval, new ConfigInstruction<GlobalConfig, uint>(config => config.HasRecordDanmakuFlushInterval = false, (config, value) => config.RecordDanmakuFlushInterval = value) { Name = "RecordDanmakuFlushInterval", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.Cookie, new ConfigInstruction<GlobalConfig, string>(config => config.HasCookie = false, (config, value) => config.Cookie = value) { Name = "Cookie", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.WebHookUrls, new ConfigInstruction<GlobalConfig, string>(config => config.HasWebHookUrls = false, (config, value) => config.WebHookUrls = value) { Name = "WebHookUrls", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.WebHookUrlsV2, new ConfigInstruction<GlobalConfig, string>(config => config.HasWebHookUrlsV2 = false, (config, value) => config.WebHookUrlsV2 = value) { Name = "WebHookUrlsV2", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.LiveApiHost, new ConfigInstruction<GlobalConfig, string>(config => config.HasLiveApiHost = false, (config, value) => config.LiveApiHost = value) { Name = "LiveApiHost", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.RecordFilenameFormat, new ConfigInstruction<GlobalConfig, string>(config => config.HasRecordFilenameFormat = false, (config, value) => config.RecordFilenameFormat = value) { Name = "RecordFilenameFormat", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.WpfShowTitleAndArea, new ConfigInstruction<GlobalConfig, bool>(config => config.HasWpfShowTitleAndArea = false, (config, value) => config.WpfShowTitleAndArea = value) { Name = "WpfShowTitleAndArea", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.RecordMode, new ConfigInstruction<GlobalConfig, RecordMode>(config => config.HasRecordMode = false, (config, value) => config.RecordMode = value) { Name = "RecordMode", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.CuttingMode, new ConfigInstruction<GlobalConfig, CuttingMode>(config => config.HasCuttingMode = false, (config, value) => config.CuttingMode = value) { Name = "CuttingMode", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.CuttingNumber, new ConfigInstruction<GlobalConfig, uint>(config => config.HasCuttingNumber = false, (config, value) => config.CuttingNumber = value) { Name = "CuttingNumber", CanBeOptional = true });
@ -79,6 +67,18 @@ namespace BililiveRecorder.Cli.Configure
GlobalConfig.Add(GlobalConfigProperties.RecordDanmakuGift, new ConfigInstruction<GlobalConfig, bool>(config => config.HasRecordDanmakuGift = false, (config, value) => config.RecordDanmakuGift = value) { Name = "RecordDanmakuGift", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.RecordDanmakuGuard, new ConfigInstruction<GlobalConfig, bool>(config => config.HasRecordDanmakuGuard = false, (config, value) => config.RecordDanmakuGuard = value) { Name = "RecordDanmakuGuard", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.RecordingQuality, new ConfigInstruction<GlobalConfig, string>(config => config.HasRecordingQuality = false, (config, value) => config.RecordingQuality = value) { Name = "RecordingQuality", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.RecordFilenameFormat, new ConfigInstruction<GlobalConfig, string>(config => config.HasRecordFilenameFormat = false, (config, value) => config.RecordFilenameFormat = value) { Name = "RecordFilenameFormat", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.WebHookUrls, new ConfigInstruction<GlobalConfig, string>(config => config.HasWebHookUrls = false, (config, value) => config.WebHookUrls = value) { Name = "WebHookUrls", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.WebHookUrlsV2, new ConfigInstruction<GlobalConfig, string>(config => config.HasWebHookUrlsV2 = false, (config, value) => config.WebHookUrlsV2 = value) { Name = "WebHookUrlsV2", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.WpfShowTitleAndArea, new ConfigInstruction<GlobalConfig, bool>(config => config.HasWpfShowTitleAndArea = false, (config, value) => config.WpfShowTitleAndArea = value) { Name = "WpfShowTitleAndArea", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.Cookie, new ConfigInstruction<GlobalConfig, string>(config => config.HasCookie = false, (config, value) => config.Cookie = value) { Name = "Cookie", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.LiveApiHost, new ConfigInstruction<GlobalConfig, string>(config => config.HasLiveApiHost = false, (config, value) => config.LiveApiHost = value) { Name = "LiveApiHost", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.TimingCheckInterval, new ConfigInstruction<GlobalConfig, uint>(config => config.HasTimingCheckInterval = false, (config, value) => config.TimingCheckInterval = value) { Name = "TimingCheckInterval", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.TimingStreamRetry, new ConfigInstruction<GlobalConfig, uint>(config => config.HasTimingStreamRetry = false, (config, value) => config.TimingStreamRetry = value) { Name = "TimingStreamRetry", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.TimingStreamConnect, new ConfigInstruction<GlobalConfig, uint>(config => config.HasTimingStreamConnect = false, (config, value) => config.TimingStreamConnect = value) { Name = "TimingStreamConnect", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.TimingDanmakuRetry, new ConfigInstruction<GlobalConfig, uint>(config => config.HasTimingDanmakuRetry = false, (config, value) => config.TimingDanmakuRetry = value) { Name = "TimingDanmakuRetry", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.TimingWatchdogTimeout, new ConfigInstruction<GlobalConfig, uint>(config => config.HasTimingWatchdogTimeout = false, (config, value) => config.TimingWatchdogTimeout = value) { Name = "TimingWatchdogTimeout", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.RecordDanmakuFlushInterval, new ConfigInstruction<GlobalConfig, uint>(config => config.HasRecordDanmakuFlushInterval = false, (config, value) => config.RecordDanmakuFlushInterval = value) { Name = "RecordDanmakuFlushInterval", CanBeOptional = true });
RoomConfig.Add(RoomConfigProperties.RoomId, new ConfigInstruction<RoomConfig, int>(config => config.HasRoomId = false, (config, value) => config.RoomId = value) { Name = "RoomId", CanBeOptional = false });
RoomConfig.Add(RoomConfigProperties.AutoRecord, new ConfigInstruction<RoomConfig, bool>(config => config.HasAutoRecord = false, (config, value) => config.AutoRecord = value) { Name = "AutoRecord", CanBeOptional = false });

View File

@ -1,6 +1,6 @@
// ******************************
// GENERATED CODE, DO NOT EDIT MANUALLY.
// SEE .tools/build_config.js
// SEE /config_gen/README.md
// ******************************
using System.ComponentModel;
@ -22,7 +22,7 @@ namespace BililiveRecorder.Core.Config.V2
public Optional<int> OptionalRoomId { get => this.GetPropertyValueOptional<int>(nameof(this.RoomId)); set => this.SetPropertyValueOptional(value, nameof(this.RoomId)); }
/// <summary>
/// 是否启用自动录制
/// 自动录制
/// </summary>
public bool AutoRecord { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
public bool HasAutoRecord { get => this.GetPropertyHasValue(nameof(this.AutoRecord)); set => this.SetPropertyHasValue<bool>(value, nameof(this.AutoRecord)); }
@ -38,7 +38,7 @@ namespace BililiveRecorder.Core.Config.V2
public Optional<RecordMode> OptionalRecordMode { get => this.GetPropertyValueOptional<RecordMode>(nameof(this.RecordMode)); set => this.SetPropertyValueOptional(value, nameof(this.RecordMode)); }
/// <summary>
/// 录制文件自动切割模式
/// 自动分段模式
/// </summary>
public CuttingMode CuttingMode { get => this.GetPropertyValue<CuttingMode>(); set => this.SetPropertyValue(value); }
public bool HasCuttingMode { get => this.GetPropertyHasValue(nameof(this.CuttingMode)); set => this.SetPropertyHasValue<CuttingMode>(value, nameof(this.CuttingMode)); }
@ -46,7 +46,7 @@ namespace BililiveRecorder.Core.Config.V2
public Optional<CuttingMode> OptionalCuttingMode { get => this.GetPropertyValueOptional<CuttingMode>(nameof(this.CuttingMode)); set => this.SetPropertyValueOptional(value, nameof(this.CuttingMode)); }
/// <summary>
/// 录制文件自动切割数值(分钟/MiB
/// 自动分段数值
/// </summary>
public uint CuttingNumber { get => this.GetPropertyValue<uint>(); set => this.SetPropertyValue(value); }
public bool HasCuttingNumber { get => this.GetPropertyHasValue(nameof(this.CuttingNumber)); set => this.SetPropertyHasValue<uint>(value, nameof(this.CuttingNumber)); }
@ -54,7 +54,7 @@ namespace BililiveRecorder.Core.Config.V2
public Optional<uint> OptionalCuttingNumber { get => this.GetPropertyValueOptional<uint>(nameof(this.CuttingNumber)); set => this.SetPropertyValueOptional(value, nameof(this.CuttingNumber)); }
/// <summary>
/// 是否同时录制弹幕
/// 弹幕录制
/// </summary>
public bool RecordDanmaku { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
public bool HasRecordDanmaku { get => this.GetPropertyHasValue(nameof(this.RecordDanmaku)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmaku)); }
@ -62,7 +62,7 @@ namespace BililiveRecorder.Core.Config.V2
public Optional<bool> OptionalRecordDanmaku { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmaku)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmaku)); }
/// <summary>
/// 是否记录弹幕原始数据
/// 弹幕录制-原始数据
/// </summary>
public bool RecordDanmakuRaw { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
public bool HasRecordDanmakuRaw { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuRaw)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmakuRaw)); }
@ -70,7 +70,7 @@ namespace BililiveRecorder.Core.Config.V2
public Optional<bool> OptionalRecordDanmakuRaw { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmakuRaw)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuRaw)); }
/// <summary>
/// 是否同时录制 SuperChat
/// 弹幕录制-SuperChat
/// </summary>
public bool RecordDanmakuSuperChat { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
public bool HasRecordDanmakuSuperChat { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuSuperChat)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmakuSuperChat)); }
@ -78,7 +78,7 @@ namespace BililiveRecorder.Core.Config.V2
public Optional<bool> OptionalRecordDanmakuSuperChat { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmakuSuperChat)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuSuperChat)); }
/// <summary>
/// 是否同时录制 礼物
/// 弹幕录制-礼物
/// </summary>
public bool RecordDanmakuGift { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
public bool HasRecordDanmakuGift { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuGift)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmakuGift)); }
@ -86,7 +86,7 @@ namespace BililiveRecorder.Core.Config.V2
public Optional<bool> OptionalRecordDanmakuGift { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmakuGift)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuGift)); }
/// <summary>
/// 是否同时录制 上船
/// 弹幕录制-上船
/// </summary>
public bool RecordDanmakuGuard { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
public bool HasRecordDanmakuGuard { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuGuard)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmakuGuard)); }
@ -94,13 +94,48 @@ namespace BililiveRecorder.Core.Config.V2
public Optional<bool> OptionalRecordDanmakuGuard { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmakuGuard)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuGuard)); }
/// <summary>
/// 录制的直播画质 qn 值,逗号分割,靠前的优先
/// 直播画质
/// </summary>
public string? RecordingQuality { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); }
public bool HasRecordingQuality { get => this.GetPropertyHasValue(nameof(this.RecordingQuality)); set => this.SetPropertyHasValue<string>(value, nameof(this.RecordingQuality)); }
[JsonProperty(nameof(RecordingQuality)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<string?> OptionalRecordingQuality { get => this.GetPropertyValueOptional<string>(nameof(this.RecordingQuality)); set => this.SetPropertyValueOptional(value, nameof(this.RecordingQuality)); }
/// <summary>
/// 录制文件名格式
/// </summary>
public string? RecordFilenameFormat => this.GetPropertyValue<string>();
/// <summary>
/// 录制文件写入结束 Webhook 地址 每行一个
/// </summary>
public string? WebHookUrls => this.GetPropertyValue<string>();
/// <summary>
/// Webhook v2 地址 每行一个
/// </summary>
public string? WebHookUrlsV2 => this.GetPropertyValue<string>();
/// <summary>
/// 在界面显示标题和分区
/// </summary>
public bool WpfShowTitleAndArea => this.GetPropertyValue<bool>();
/// <summary>
/// 请求 API 时使用的 Cookie
/// </summary>
public string? Cookie => this.GetPropertyValue<string>();
/// <summary>
/// 替换 api.live.bilibili.com 服务器为其他反代,可以支持在云服务器上录制
/// </summary>
public string? LiveApiHost => this.GetPropertyValue<string>();
/// <summary>
/// HTTP API 检查时间间隔 秒
/// </summary>
public uint TimingCheckInterval => this.GetPropertyValue<uint>();
/// <summary>
/// 录制断开重连时间间隔 毫秒
/// </summary>
@ -117,12 +152,7 @@ namespace BililiveRecorder.Core.Config.V2
public uint TimingDanmakuRetry => this.GetPropertyValue<uint>();
/// <summary>
/// HTTP API 检查时间间隔 秒
/// </summary>
public uint TimingCheckInterval => this.GetPropertyValue<uint>();
/// <summary>
/// 最大未收到新直播数据时间 毫秒
/// 最大允许未收到直播数据时间 毫秒
/// </summary>
public uint TimingWatchdogTimeout => this.GetPropertyValue<uint>();
@ -131,41 +161,139 @@ namespace BililiveRecorder.Core.Config.V2
/// </summary>
public uint RecordDanmakuFlushInterval => this.GetPropertyValue<uint>();
/// <summary>
/// 请求 API 时使用的 Cookie
/// </summary>
public string? Cookie => this.GetPropertyValue<string>();
/// <summary>
/// 录制文件写入结束 Webhook 地址 每行一个
/// </summary>
public string? WebHookUrls => this.GetPropertyValue<string>();
/// <summary>
/// Webhook v2 地址 每行一个
/// </summary>
public string? WebHookUrlsV2 => this.GetPropertyValue<string>();
/// <summary>
/// 替换 api.live.bilibili.com 服务器为其他反代,可以支持在云服务器上录制
/// </summary>
public string? LiveApiHost => this.GetPropertyValue<string>();
/// <summary>
/// 录制文件名模板
/// </summary>
public string? RecordFilenameFormat => this.GetPropertyValue<string>();
/// <summary>
/// 是否显示直播间标题和分区
/// </summary>
public bool WpfShowTitleAndArea => this.GetPropertyValue<bool>();
}
[JsonObject(MemberSerialization.OptIn)]
public sealed partial class GlobalConfig : HierarchicalObject<DefaultConfig, GlobalConfig>
{
/// <summary>
/// 录制模式
/// </summary>
public RecordMode RecordMode { get => this.GetPropertyValue<RecordMode>(); set => this.SetPropertyValue(value); }
public bool HasRecordMode { get => this.GetPropertyHasValue(nameof(this.RecordMode)); set => this.SetPropertyHasValue<RecordMode>(value, nameof(this.RecordMode)); }
[JsonProperty(nameof(RecordMode)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<RecordMode> OptionalRecordMode { get => this.GetPropertyValueOptional<RecordMode>(nameof(this.RecordMode)); set => this.SetPropertyValueOptional(value, nameof(this.RecordMode)); }
/// <summary>
/// 自动分段模式
/// </summary>
public CuttingMode CuttingMode { get => this.GetPropertyValue<CuttingMode>(); set => this.SetPropertyValue(value); }
public bool HasCuttingMode { get => this.GetPropertyHasValue(nameof(this.CuttingMode)); set => this.SetPropertyHasValue<CuttingMode>(value, nameof(this.CuttingMode)); }
[JsonProperty(nameof(CuttingMode)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<CuttingMode> OptionalCuttingMode { get => this.GetPropertyValueOptional<CuttingMode>(nameof(this.CuttingMode)); set => this.SetPropertyValueOptional(value, nameof(this.CuttingMode)); }
/// <summary>
/// 自动分段数值
/// </summary>
public uint CuttingNumber { get => this.GetPropertyValue<uint>(); set => this.SetPropertyValue(value); }
public bool HasCuttingNumber { get => this.GetPropertyHasValue(nameof(this.CuttingNumber)); set => this.SetPropertyHasValue<uint>(value, nameof(this.CuttingNumber)); }
[JsonProperty(nameof(CuttingNumber)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<uint> OptionalCuttingNumber { get => this.GetPropertyValueOptional<uint>(nameof(this.CuttingNumber)); set => this.SetPropertyValueOptional(value, nameof(this.CuttingNumber)); }
/// <summary>
/// 弹幕录制
/// </summary>
public bool RecordDanmaku { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
public bool HasRecordDanmaku { get => this.GetPropertyHasValue(nameof(this.RecordDanmaku)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmaku)); }
[JsonProperty(nameof(RecordDanmaku)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<bool> OptionalRecordDanmaku { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmaku)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmaku)); }
/// <summary>
/// 弹幕录制-原始数据
/// </summary>
public bool RecordDanmakuRaw { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
public bool HasRecordDanmakuRaw { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuRaw)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmakuRaw)); }
[JsonProperty(nameof(RecordDanmakuRaw)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<bool> OptionalRecordDanmakuRaw { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmakuRaw)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuRaw)); }
/// <summary>
/// 弹幕录制-SuperChat
/// </summary>
public bool RecordDanmakuSuperChat { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
public bool HasRecordDanmakuSuperChat { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuSuperChat)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmakuSuperChat)); }
[JsonProperty(nameof(RecordDanmakuSuperChat)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<bool> OptionalRecordDanmakuSuperChat { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmakuSuperChat)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuSuperChat)); }
/// <summary>
/// 弹幕录制-礼物
/// </summary>
public bool RecordDanmakuGift { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
public bool HasRecordDanmakuGift { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuGift)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmakuGift)); }
[JsonProperty(nameof(RecordDanmakuGift)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<bool> OptionalRecordDanmakuGift { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmakuGift)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuGift)); }
/// <summary>
/// 弹幕录制-上船
/// </summary>
public bool RecordDanmakuGuard { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
public bool HasRecordDanmakuGuard { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuGuard)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmakuGuard)); }
[JsonProperty(nameof(RecordDanmakuGuard)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<bool> OptionalRecordDanmakuGuard { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmakuGuard)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuGuard)); }
/// <summary>
/// 直播画质
/// </summary>
public string? RecordingQuality { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); }
public bool HasRecordingQuality { get => this.GetPropertyHasValue(nameof(this.RecordingQuality)); set => this.SetPropertyHasValue<string>(value, nameof(this.RecordingQuality)); }
[JsonProperty(nameof(RecordingQuality)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<string?> OptionalRecordingQuality { get => this.GetPropertyValueOptional<string>(nameof(this.RecordingQuality)); set => this.SetPropertyValueOptional(value, nameof(this.RecordingQuality)); }
/// <summary>
/// 录制文件名格式
/// </summary>
public string? RecordFilenameFormat { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); }
public bool HasRecordFilenameFormat { get => this.GetPropertyHasValue(nameof(this.RecordFilenameFormat)); set => this.SetPropertyHasValue<string>(value, nameof(this.RecordFilenameFormat)); }
[JsonProperty(nameof(RecordFilenameFormat)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<string?> OptionalRecordFilenameFormat { get => this.GetPropertyValueOptional<string>(nameof(this.RecordFilenameFormat)); set => this.SetPropertyValueOptional(value, nameof(this.RecordFilenameFormat)); }
/// <summary>
/// 录制文件写入结束 Webhook 地址 每行一个
/// </summary>
public string? WebHookUrls { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); }
public bool HasWebHookUrls { get => this.GetPropertyHasValue(nameof(this.WebHookUrls)); set => this.SetPropertyHasValue<string>(value, nameof(this.WebHookUrls)); }
[JsonProperty(nameof(WebHookUrls)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<string?> OptionalWebHookUrls { get => this.GetPropertyValueOptional<string>(nameof(this.WebHookUrls)); set => this.SetPropertyValueOptional(value, nameof(this.WebHookUrls)); }
/// <summary>
/// Webhook v2 地址 每行一个
/// </summary>
public string? WebHookUrlsV2 { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); }
public bool HasWebHookUrlsV2 { get => this.GetPropertyHasValue(nameof(this.WebHookUrlsV2)); set => this.SetPropertyHasValue<string>(value, nameof(this.WebHookUrlsV2)); }
[JsonProperty(nameof(WebHookUrlsV2)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<string?> OptionalWebHookUrlsV2 { get => this.GetPropertyValueOptional<string>(nameof(this.WebHookUrlsV2)); set => this.SetPropertyValueOptional(value, nameof(this.WebHookUrlsV2)); }
/// <summary>
/// 在界面显示标题和分区
/// </summary>
public bool WpfShowTitleAndArea { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
public bool HasWpfShowTitleAndArea { get => this.GetPropertyHasValue(nameof(this.WpfShowTitleAndArea)); set => this.SetPropertyHasValue<bool>(value, nameof(this.WpfShowTitleAndArea)); }
[JsonProperty(nameof(WpfShowTitleAndArea)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<bool> OptionalWpfShowTitleAndArea { get => this.GetPropertyValueOptional<bool>(nameof(this.WpfShowTitleAndArea)); set => this.SetPropertyValueOptional(value, nameof(this.WpfShowTitleAndArea)); }
/// <summary>
/// 请求 API 时使用的 Cookie
/// </summary>
public string? Cookie { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); }
public bool HasCookie { get => this.GetPropertyHasValue(nameof(this.Cookie)); set => this.SetPropertyHasValue<string>(value, nameof(this.Cookie)); }
[JsonProperty(nameof(Cookie)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<string?> OptionalCookie { get => this.GetPropertyValueOptional<string>(nameof(this.Cookie)); set => this.SetPropertyValueOptional(value, nameof(this.Cookie)); }
/// <summary>
/// 替换 api.live.bilibili.com 服务器为其他反代,可以支持在云服务器上录制
/// </summary>
public string? LiveApiHost { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); }
public bool HasLiveApiHost { get => this.GetPropertyHasValue(nameof(this.LiveApiHost)); set => this.SetPropertyHasValue<string>(value, nameof(this.LiveApiHost)); }
[JsonProperty(nameof(LiveApiHost)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<string?> OptionalLiveApiHost { get => this.GetPropertyValueOptional<string>(nameof(this.LiveApiHost)); set => this.SetPropertyValueOptional(value, nameof(this.LiveApiHost)); }
/// <summary>
/// HTTP API 检查时间间隔 秒
/// </summary>
public uint TimingCheckInterval { get => this.GetPropertyValue<uint>(); set => this.SetPropertyValue(value); }
public bool HasTimingCheckInterval { get => this.GetPropertyHasValue(nameof(this.TimingCheckInterval)); set => this.SetPropertyHasValue<uint>(value, nameof(this.TimingCheckInterval)); }
[JsonProperty(nameof(TimingCheckInterval)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<uint> OptionalTimingCheckInterval { get => this.GetPropertyValueOptional<uint>(nameof(this.TimingCheckInterval)); set => this.SetPropertyValueOptional(value, nameof(this.TimingCheckInterval)); }
/// <summary>
/// 录制断开重连时间间隔 毫秒
/// </summary>
@ -191,15 +319,7 @@ namespace BililiveRecorder.Core.Config.V2
public Optional<uint> OptionalTimingDanmakuRetry { get => this.GetPropertyValueOptional<uint>(nameof(this.TimingDanmakuRetry)); set => this.SetPropertyValueOptional(value, nameof(this.TimingDanmakuRetry)); }
/// <summary>
/// HTTP API 检查时间间隔 秒
/// </summary>
public uint TimingCheckInterval { get => this.GetPropertyValue<uint>(); set => this.SetPropertyValue(value); }
public bool HasTimingCheckInterval { get => this.GetPropertyHasValue(nameof(this.TimingCheckInterval)); set => this.SetPropertyHasValue<uint>(value, nameof(this.TimingCheckInterval)); }
[JsonProperty(nameof(TimingCheckInterval)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<uint> OptionalTimingCheckInterval { get => this.GetPropertyValueOptional<uint>(nameof(this.TimingCheckInterval)); set => this.SetPropertyValueOptional(value, nameof(this.TimingCheckInterval)); }
/// <summary>
/// 最大未收到新直播数据时间 毫秒
/// 最大允许未收到直播数据时间 毫秒
/// </summary>
public uint TimingWatchdogTimeout { get => this.GetPropertyValue<uint>(); set => this.SetPropertyValue(value); }
public bool HasTimingWatchdogTimeout { get => this.GetPropertyHasValue(nameof(this.TimingWatchdogTimeout)); set => this.SetPropertyHasValue<uint>(value, nameof(this.TimingWatchdogTimeout)); }
@ -214,126 +334,6 @@ namespace BililiveRecorder.Core.Config.V2
[JsonProperty(nameof(RecordDanmakuFlushInterval)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<uint> OptionalRecordDanmakuFlushInterval { get => this.GetPropertyValueOptional<uint>(nameof(this.RecordDanmakuFlushInterval)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuFlushInterval)); }
/// <summary>
/// 请求 API 时使用的 Cookie
/// </summary>
public string? Cookie { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); }
public bool HasCookie { get => this.GetPropertyHasValue(nameof(this.Cookie)); set => this.SetPropertyHasValue<string>(value, nameof(this.Cookie)); }
[JsonProperty(nameof(Cookie)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<string?> OptionalCookie { get => this.GetPropertyValueOptional<string>(nameof(this.Cookie)); set => this.SetPropertyValueOptional(value, nameof(this.Cookie)); }
/// <summary>
/// 录制文件写入结束 Webhook 地址 每行一个
/// </summary>
public string? WebHookUrls { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); }
public bool HasWebHookUrls { get => this.GetPropertyHasValue(nameof(this.WebHookUrls)); set => this.SetPropertyHasValue<string>(value, nameof(this.WebHookUrls)); }
[JsonProperty(nameof(WebHookUrls)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<string?> OptionalWebHookUrls { get => this.GetPropertyValueOptional<string>(nameof(this.WebHookUrls)); set => this.SetPropertyValueOptional(value, nameof(this.WebHookUrls)); }
/// <summary>
/// Webhook v2 地址 每行一个
/// </summary>
public string? WebHookUrlsV2 { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); }
public bool HasWebHookUrlsV2 { get => this.GetPropertyHasValue(nameof(this.WebHookUrlsV2)); set => this.SetPropertyHasValue<string>(value, nameof(this.WebHookUrlsV2)); }
[JsonProperty(nameof(WebHookUrlsV2)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<string?> OptionalWebHookUrlsV2 { get => this.GetPropertyValueOptional<string>(nameof(this.WebHookUrlsV2)); set => this.SetPropertyValueOptional(value, nameof(this.WebHookUrlsV2)); }
/// <summary>
/// 替换 api.live.bilibili.com 服务器为其他反代,可以支持在云服务器上录制
/// </summary>
public string? LiveApiHost { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); }
public bool HasLiveApiHost { get => this.GetPropertyHasValue(nameof(this.LiveApiHost)); set => this.SetPropertyHasValue<string>(value, nameof(this.LiveApiHost)); }
[JsonProperty(nameof(LiveApiHost)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<string?> OptionalLiveApiHost { get => this.GetPropertyValueOptional<string>(nameof(this.LiveApiHost)); set => this.SetPropertyValueOptional(value, nameof(this.LiveApiHost)); }
/// <summary>
/// 录制文件名模板
/// </summary>
public string? RecordFilenameFormat { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); }
public bool HasRecordFilenameFormat { get => this.GetPropertyHasValue(nameof(this.RecordFilenameFormat)); set => this.SetPropertyHasValue<string>(value, nameof(this.RecordFilenameFormat)); }
[JsonProperty(nameof(RecordFilenameFormat)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<string?> OptionalRecordFilenameFormat { get => this.GetPropertyValueOptional<string>(nameof(this.RecordFilenameFormat)); set => this.SetPropertyValueOptional(value, nameof(this.RecordFilenameFormat)); }
/// <summary>
/// 是否显示直播间标题和分区
/// </summary>
public bool WpfShowTitleAndArea { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
public bool HasWpfShowTitleAndArea { get => this.GetPropertyHasValue(nameof(this.WpfShowTitleAndArea)); set => this.SetPropertyHasValue<bool>(value, nameof(this.WpfShowTitleAndArea)); }
[JsonProperty(nameof(WpfShowTitleAndArea)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<bool> OptionalWpfShowTitleAndArea { get => this.GetPropertyValueOptional<bool>(nameof(this.WpfShowTitleAndArea)); set => this.SetPropertyValueOptional(value, nameof(this.WpfShowTitleAndArea)); }
/// <summary>
/// 录制模式
/// </summary>
public RecordMode RecordMode { get => this.GetPropertyValue<RecordMode>(); set => this.SetPropertyValue(value); }
public bool HasRecordMode { get => this.GetPropertyHasValue(nameof(this.RecordMode)); set => this.SetPropertyHasValue<RecordMode>(value, nameof(this.RecordMode)); }
[JsonProperty(nameof(RecordMode)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<RecordMode> OptionalRecordMode { get => this.GetPropertyValueOptional<RecordMode>(nameof(this.RecordMode)); set => this.SetPropertyValueOptional(value, nameof(this.RecordMode)); }
/// <summary>
/// 录制文件自动切割模式
/// </summary>
public CuttingMode CuttingMode { get => this.GetPropertyValue<CuttingMode>(); set => this.SetPropertyValue(value); }
public bool HasCuttingMode { get => this.GetPropertyHasValue(nameof(this.CuttingMode)); set => this.SetPropertyHasValue<CuttingMode>(value, nameof(this.CuttingMode)); }
[JsonProperty(nameof(CuttingMode)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<CuttingMode> OptionalCuttingMode { get => this.GetPropertyValueOptional<CuttingMode>(nameof(this.CuttingMode)); set => this.SetPropertyValueOptional(value, nameof(this.CuttingMode)); }
/// <summary>
/// 录制文件自动切割数值(分钟/MiB
/// </summary>
public uint CuttingNumber { get => this.GetPropertyValue<uint>(); set => this.SetPropertyValue(value); }
public bool HasCuttingNumber { get => this.GetPropertyHasValue(nameof(this.CuttingNumber)); set => this.SetPropertyHasValue<uint>(value, nameof(this.CuttingNumber)); }
[JsonProperty(nameof(CuttingNumber)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<uint> OptionalCuttingNumber { get => this.GetPropertyValueOptional<uint>(nameof(this.CuttingNumber)); set => this.SetPropertyValueOptional(value, nameof(this.CuttingNumber)); }
/// <summary>
/// 是否同时录制弹幕
/// </summary>
public bool RecordDanmaku { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
public bool HasRecordDanmaku { get => this.GetPropertyHasValue(nameof(this.RecordDanmaku)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmaku)); }
[JsonProperty(nameof(RecordDanmaku)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<bool> OptionalRecordDanmaku { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmaku)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmaku)); }
/// <summary>
/// 是否记录弹幕原始数据
/// </summary>
public bool RecordDanmakuRaw { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
public bool HasRecordDanmakuRaw { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuRaw)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmakuRaw)); }
[JsonProperty(nameof(RecordDanmakuRaw)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<bool> OptionalRecordDanmakuRaw { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmakuRaw)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuRaw)); }
/// <summary>
/// 是否同时录制 SuperChat
/// </summary>
public bool RecordDanmakuSuperChat { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
public bool HasRecordDanmakuSuperChat { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuSuperChat)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmakuSuperChat)); }
[JsonProperty(nameof(RecordDanmakuSuperChat)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<bool> OptionalRecordDanmakuSuperChat { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmakuSuperChat)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuSuperChat)); }
/// <summary>
/// 是否同时录制 礼物
/// </summary>
public bool RecordDanmakuGift { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
public bool HasRecordDanmakuGift { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuGift)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmakuGift)); }
[JsonProperty(nameof(RecordDanmakuGift)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<bool> OptionalRecordDanmakuGift { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmakuGift)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuGift)); }
/// <summary>
/// 是否同时录制 上船
/// </summary>
public bool RecordDanmakuGuard { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
public bool HasRecordDanmakuGuard { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuGuard)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmakuGuard)); }
[JsonProperty(nameof(RecordDanmakuGuard)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<bool> OptionalRecordDanmakuGuard { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmakuGuard)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuGuard)); }
/// <summary>
/// 录制的直播画质 qn 值,逗号分割,靠前的优先
/// </summary>
public string? RecordingQuality { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); }
public bool HasRecordingQuality { get => this.GetPropertyHasValue(nameof(this.RecordingQuality)); set => this.SetPropertyHasValue<string>(value, nameof(this.RecordingQuality)); }
[JsonProperty(nameof(RecordingQuality)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<string?> OptionalRecordingQuality { get => this.GetPropertyValueOptional<string>(nameof(this.RecordingQuality)); set => this.SetPropertyValueOptional(value, nameof(this.RecordingQuality)); }
}
public sealed partial class DefaultConfig
@ -341,30 +341,6 @@ namespace BililiveRecorder.Core.Config.V2
public static readonly DefaultConfig Instance = new DefaultConfig();
private DefaultConfig() { }
public uint TimingStreamRetry => 6 * 1000;
public uint TimingStreamConnect => 5 * 1000;
public uint TimingDanmakuRetry => 15 * 1000;
public uint TimingCheckInterval => 10 * 60;
public uint TimingWatchdogTimeout => 10 * 1000;
public uint RecordDanmakuFlushInterval => 20;
public string Cookie => string.Empty;
public string WebHookUrls => string.Empty;
public string WebHookUrlsV2 => string.Empty;
public string LiveApiHost => "https://api.live.bilibili.com";
public string RecordFilenameFormat => @"{roomid}-{name}/录制-{roomid}-{date}-{time}-{ms}-{title}.flv";
public bool WpfShowTitleAndArea => true;
public RecordMode RecordMode => RecordMode.Standard;
public CuttingMode CuttingMode => CuttingMode.Disabled;
@ -383,6 +359,30 @@ namespace BililiveRecorder.Core.Config.V2
public string RecordingQuality => "10000";
public string RecordFilenameFormat => @"{roomid}-{name}/录制-{roomid}-{date}-{time}-{ms}-{title}.flv";
public string WebHookUrls => string.Empty;
public string WebHookUrlsV2 => string.Empty;
public bool WpfShowTitleAndArea => true;
public string Cookie => string.Empty;
public string LiveApiHost => "https://api.live.bilibili.com";
public uint TimingCheckInterval => 10 * 60;
public uint TimingStreamRetry => 6 * 1000;
public uint TimingStreamConnect => 5 * 1000;
public uint TimingDanmakuRetry => 9 * 1000;
public uint TimingWatchdogTimeout => 10 * 1000;
public uint RecordDanmakuFlushInterval => 20;
}
}

View File

@ -3,11 +3,127 @@
"$schema": "http://json-schema.org/schema",
"definitions": {
"global-config": {
"description": "全局置",
"description": "全局置",
"additionalProperties": false,
"properties": {
"RecordFilenameFormat": {
"description": "录制文件名格式\n默认: {roomid}-{name}/录制-{roomid}-{date}-{time}-{ms}-{title}.flv",
"markdownDescription": "录制文件名格式 \n默认: `{roomid}-{name}/录制-{roomid}-{date}-{time}-{ms}-{title}.flv `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
"HasValue": {
"type": "boolean",
"default": true
},
"Value": {
"type": "string",
"default": "{roomid}-{name}/录制-{roomid}-{date}-{time}-{ms}-{title}.flv"
}
}
},
"WebHookUrls": {
"description": "WebhookV1\n默认: ",
"markdownDescription": "WebhookV1 \n默认: ` `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
"HasValue": {
"type": "boolean",
"default": true
},
"Value": {
"type": "string",
"default": ""
}
}
},
"WebHookUrlsV2": {
"description": "WebhookV2\n默认: ",
"markdownDescription": "WebhookV2 \n默认: ` `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
"HasValue": {
"type": "boolean",
"default": true
},
"Value": {
"type": "string",
"default": ""
}
}
},
"WpfShowTitleAndArea": {
"description": "在界面显示标题和分区\n默认: true",
"markdownDescription": "在界面显示标题和分区 \n默认: `true `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
"HasValue": {
"type": "boolean",
"default": true
},
"Value": {
"type": "boolean",
"default": true
}
}
},
"Cookie": {
"description": "请求 API 时使用的 Cookie\n默认: (空字符串)",
"markdownDescription": "请求 API 时使用的 Cookie \n默认: `(空字符串) `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
"HasValue": {
"type": "boolean",
"default": true
},
"Value": {
"type": "string",
"pattern": "^(S+=S+;? ?)*$",
"maxLength": 4096
}
}
},
"LiveApiHost": {
"description": "请求的 API Host\n默认: https://api.live.bilibili.com",
"markdownDescription": "请求的 API Host \n默认: `https://api.live.bilibili.com `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
"HasValue": {
"type": "boolean",
"default": true
},
"Value": {
"type": "string",
"default": "https://api.live.bilibili.com"
}
}
},
"TimingCheckInterval": {
"description": "HTTP API 检查时间间隔 秒\n默认: 600 (10分)",
"markdownDescription": "HTTP API 检查时间间隔 秒 \n默认: `600 (10分) `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
"HasValue": {
"type": "boolean",
"default": true
},
"Value": {
"type": "integer",
"minimum": 0,
"maximum": 4294967295,
"default": 600
}
}
},
"TimingStreamRetry": {
"description": "录制断开重连时间间隔 毫秒",
"description": "录制断开重连时间间隔 毫秒\n默认: 6000 (6秒)",
"markdownDescription": "录制断开重连时间间隔 毫秒 \n默认: `6000 (6秒) `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -24,7 +140,8 @@
}
},
"TimingStreamConnect": {
"description": "连接直播服务器超时时间 毫秒",
"description": "连接直播服务器超时时间 毫秒\n默认: 5000 (5秒)",
"markdownDescription": "连接直播服务器超时时间 毫秒 \n默认: `5000 (5秒) `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -41,7 +158,8 @@
}
},
"TimingDanmakuRetry": {
"description": "弹幕服务器重连时间间隔 毫秒",
"description": "弹幕服务器重连时间间隔 毫秒\n默认: 9000 (9秒)",
"markdownDescription": "弹幕服务器重连时间间隔 毫秒 \n默认: `9000 (9秒) `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -53,29 +171,13 @@
"type": "integer",
"minimum": 0,
"maximum": 4294967295,
"default": 15000
}
}
},
"TimingCheckInterval": {
"description": "HTTP API 检查时间间隔 秒",
"type": "object",
"additionalProperties": false,
"properties": {
"HasValue": {
"type": "boolean",
"default": true
},
"Value": {
"type": "integer",
"minimum": 0,
"maximum": 4294967295,
"default": 600
"default": 9000
}
}
},
"TimingWatchdogTimeout": {
"description": "最大未收到新直播数据时间 毫秒",
"description": "最大允许未收到直播数据时间 毫秒\n默认: 10000 (10秒)",
"markdownDescription": "最大允许未收到直播数据时间 毫秒 \n默认: `10000 (10秒) `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -92,7 +194,8 @@
}
},
"RecordDanmakuFlushInterval": {
"description": "触发 <see cref=\"System.Xml.XmlWriter.Flush\"/> 的弹幕个数",
"description": "触发刷新弹幕写入缓冲的个数\n默认: 20",
"markdownDescription": "触发刷新弹幕写入缓冲的个数 \n默认: `20 `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -108,99 +211,9 @@
}
}
},
"Cookie": {
"description": "请求 API 时使用的 Cookie",
"type": "object",
"additionalProperties": false,
"properties": {
"HasValue": {
"type": "boolean",
"default": true
},
"Value": {
"type": "string",
"pattern": "^(S+=S+;? ?)*$",
"maxLength": 4096
}
}
},
"WebHookUrls": {
"description": "录制文件写入结束 Webhook 地址 每行一个",
"type": "object",
"additionalProperties": false,
"properties": {
"HasValue": {
"type": "boolean",
"default": true
},
"Value": {
"type": "string",
"default": ""
}
}
},
"WebHookUrlsV2": {
"description": "Webhook v2 地址 每行一个",
"type": "object",
"additionalProperties": false,
"properties": {
"HasValue": {
"type": "boolean",
"default": true
},
"Value": {
"type": "string",
"default": ""
}
}
},
"LiveApiHost": {
"description": "替换 api.live.bilibili.com 服务器为其他反代,可以支持在云服务器上录制",
"type": "object",
"additionalProperties": false,
"properties": {
"HasValue": {
"type": "boolean",
"default": true
},
"Value": {
"type": "string",
"default": "https://api.live.bilibili.com"
}
}
},
"RecordFilenameFormat": {
"description": "录制文件名模板",
"type": "object",
"additionalProperties": false,
"properties": {
"HasValue": {
"type": "boolean",
"default": true
},
"Value": {
"type": "string",
"default": "{roomid}-{name}/录制-{roomid}-{date}-{time}-{ms}-{title}.flv"
}
}
},
"WpfShowTitleAndArea": {
"description": "是否显示直播间标题和分区",
"type": "object",
"additionalProperties": false,
"properties": {
"HasValue": {
"type": "boolean",
"default": true
},
"Value": {
"type": "boolean",
"default": true
}
}
},
"RecordMode": {
"description": "录制模式",
"description": "录制模式\n默认: RecordMode.Standard",
"markdownDescription": "录制模式 \n默认: `RecordMode.Standard `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -220,7 +233,8 @@
}
},
"CuttingMode": {
"description": "录制文件自动切割模式",
"description": "自动分段模式\n默认: CuttingMode.Disabled",
"markdownDescription": "自动分段模式 \n默认: `CuttingMode.Disabled `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -241,7 +255,8 @@
}
},
"CuttingNumber": {
"description": "录制文件自动切割数值(分钟/MiB",
"description": "自动分段数值\n默认: 100",
"markdownDescription": "自动分段数值 \n默认: `100 `\n\n按时长分段时为分钟按大小分段时为MiB",
"type": "object",
"additionalProperties": false,
"properties": {
@ -258,7 +273,8 @@
}
},
"RecordDanmaku": {
"description": "是否同时录制弹幕",
"description": "弹幕录制\n默认: false",
"markdownDescription": "弹幕录制 \n默认: `false `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -273,7 +289,8 @@
}
},
"RecordDanmakuRaw": {
"description": "是否记录弹幕原始数据",
"description": "弹幕录制-原始数据\n默认: false",
"markdownDescription": "弹幕录制-原始数据 \n默认: `false `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -288,7 +305,8 @@
}
},
"RecordDanmakuSuperChat": {
"description": "是否同时录制 SuperChat",
"description": "弹幕录制-SuperChat\n默认: true",
"markdownDescription": "弹幕录制-SuperChat \n默认: `true `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -303,7 +321,8 @@
}
},
"RecordDanmakuGift": {
"description": "是否同时录制 礼物",
"description": "弹幕录制-礼物\n默认: false",
"markdownDescription": "弹幕录制-礼物 \n默认: `false `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -318,7 +337,8 @@
}
},
"RecordDanmakuGuard": {
"description": "是否同时录制 上船",
"description": "弹幕录制-上船\n默认: true",
"markdownDescription": "弹幕录制-上船 \n默认: `true `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -333,7 +353,8 @@
}
},
"RecordingQuality": {
"description": "录制的直播画质 qn 值,逗号分割,靠前的优先",
"description": "直播画质\n默认: 10000",
"markdownDescription": "直播画质 \n默认: `10000 `\n\n录制的直播画质 qn 值,逗号分割,靠前的优先",
"type": "object",
"additionalProperties": false,
"properties": {
@ -350,11 +371,12 @@
}
},
"room-config": {
"description": "单个房间配置",
"description": "房间独立设置",
"additionalProperties": false,
"properties": {
"RoomId": {
"description": "房间号",
"description": "房间号\n默认: default",
"markdownDescription": "房间号 \n默认: `default `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -370,7 +392,8 @@
}
},
"AutoRecord": {
"description": "是否启用自动录制",
"description": "自动录制\n默认: default",
"markdownDescription": "自动录制 \n默认: `default `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -384,7 +407,8 @@
}
},
"RecordMode": {
"description": "录制模式",
"description": "录制模式\n默认: RecordMode.Standard",
"markdownDescription": "录制模式 \n默认: `RecordMode.Standard `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -404,7 +428,8 @@
}
},
"CuttingMode": {
"description": "录制文件自动切割模式",
"description": "自动分段模式\n默认: CuttingMode.Disabled",
"markdownDescription": "自动分段模式 \n默认: `CuttingMode.Disabled `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -425,7 +450,8 @@
}
},
"CuttingNumber": {
"description": "录制文件自动切割数值(分钟/MiB",
"description": "自动分段数值\n默认: 100",
"markdownDescription": "自动分段数值 \n默认: `100 `\n\n按时长分段时为分钟按大小分段时为MiB",
"type": "object",
"additionalProperties": false,
"properties": {
@ -442,7 +468,8 @@
}
},
"RecordDanmaku": {
"description": "是否同时录制弹幕",
"description": "弹幕录制\n默认: false",
"markdownDescription": "弹幕录制 \n默认: `false `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -457,7 +484,8 @@
}
},
"RecordDanmakuRaw": {
"description": "是否记录弹幕原始数据",
"description": "弹幕录制-原始数据\n默认: false",
"markdownDescription": "弹幕录制-原始数据 \n默认: `false `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -472,7 +500,8 @@
}
},
"RecordDanmakuSuperChat": {
"description": "是否同时录制 SuperChat",
"description": "弹幕录制-SuperChat\n默认: true",
"markdownDescription": "弹幕录制-SuperChat \n默认: `true `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -487,7 +516,8 @@
}
},
"RecordDanmakuGift": {
"description": "是否同时录制 礼物",
"description": "弹幕录制-礼物\n默认: false",
"markdownDescription": "弹幕录制-礼物 \n默认: `false `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -502,7 +532,8 @@
}
},
"RecordDanmakuGuard": {
"description": "是否同时录制 上船",
"description": "弹幕录制-上船\n默认: true",
"markdownDescription": "弹幕录制-上船 \n默认: `true `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -517,7 +548,8 @@
}
},
"RecordingQuality": {
"description": "录制的直播画质 qn 值,逗号分割,靠前的优先",
"description": "直播画质\n默认: 10000",
"markdownDescription": "直播画质 \n默认: `10000 `\n\n录制的直播画质 qn 值,逗号分割,靠前的优先",
"type": "object",
"additionalProperties": false,
"properties": {

208
config_gen/data.ts Normal file
View File

@ -0,0 +1,208 @@
import { ConfigEntry, ConfigEntryType } from './types'
export const data: Array<ConfigEntry> = [
{
name: "RoomId",
description: "房间号",
type: "int",
configType: "roomOnly",
defaultValue: "default",
// web_readonly: true,
markdown: ""
},
{
name: "AutoRecord",
description: "自动录制",
type: "bool",
configType: "roomOnly",
defaultValue: "default",
markdown: ""
},
{
name: "RecordMode",
description: "录制模式",
type: "RecordMode",
configType: "room",
defaultValue: "RecordMode.Standard",
markdown: ""
},
{
name: "CuttingMode",
description: "自动分段模式",
type: "CuttingMode",
configType: "room",
defaultValue: "CuttingMode.Disabled",
markdown: ""
},
{
name: "CuttingNumber",
description: "自动分段数值",
type: "uint",
configType: "room",
defaultValue: "100",
markdown: "按时长分段时为分钟按大小分段时为MiB"
},
{
name: "RecordDanmaku",
description: "弹幕录制",
type: "bool",
configType: "room",
defaultValue: "false",
markdown: ""
},
{
name: "RecordDanmakuRaw",
description: "弹幕录制-原始数据",
type: "bool",
configType: "room",
defaultValue: "false",
markdown: ""
},
{
name: "RecordDanmakuSuperChat",
description: "弹幕录制-SuperChat",
type: "bool",
configType: "room",
defaultValue: "true",
markdown: ""
},
{
name: "RecordDanmakuGift",
description: "弹幕录制-礼物",
type: "bool",
configType: "room",
defaultValue: "false",
markdown: ""
},
{
name: "RecordDanmakuGuard",
description: "弹幕录制-上船",
type: "bool",
configType: "room",
defaultValue: "true",
markdown: ""
},
{
name: "RecordingQuality",
type: "string?",
configType: "room",
description: "直播画质",
defaultValue: "\"10000\"",
defaultValueDescription: "10000",
markdown: "录制的直播画质 qn 值,逗号分割,靠前的优先"
},
{
name: "RecordFilenameFormat",
description: "录制文件名格式",
type: "string?",
configType: "globalOnly",
defaultValue: "@\"{roomid}-{name}/录制-{roomid}-{date}-{time}-{ms}-{title}.flv\"",
markdown: ""
},
{
name: "WebHookUrls",
description: "WebhookV1",
type: "string?",
configType: "globalOnly",
defaultValue: "string.Empty",
xmlComment: "录制文件写入结束 Webhook 地址 每行一个",
markdown: ""
},
{
name: "WebHookUrlsV2",
description: "WebhookV2",
type: "string?",
configType: "globalOnly",
defaultValue: "string.Empty",
xmlComment: "Webhook v2 地址 每行一个",
markdown: ""
},
{
name: "WpfShowTitleAndArea",
description: "在界面显示标题和分区",
type: "bool",
configType: "globalOnly",
defaultValue: "true",
markdown: ""
},
{
name: "Cookie",
description: "请求 API 时使用的 Cookie",
type: "string?",
configType: "globalOnly",
advancedConfig: true,
defaultValue: "string.Empty",
defaultValueDescription: "(空字符串)",
markdown: ""
},
{
name: "LiveApiHost",
description: "请求的 API Host",
type: "string?",
configType: "globalOnly",
advancedConfig: true,
defaultValue: "\"https://api.live.bilibili.com\"",
xmlComment: "替换 api.live.bilibili.com 服务器为其他反代,可以支持在云服务器上录制",
markdown: ""
},
{
name: "TimingCheckInterval",
description: "HTTP API 检查时间间隔 秒",
type: "uint",
configType: "globalOnly",
advancedConfig: true,
defaultValue: "10 * 60",
defaultValueDescription: "600 (10分)",
markdown: ""
},
{
name: "TimingStreamRetry",
description: "录制断开重连时间间隔 毫秒",
type: "uint",
configType: "globalOnly",
advancedConfig: true,
defaultValue: "6 * 1000",
defaultValueDescription: "6000 (6秒)",
markdown: ""
},
{
name: "TimingStreamConnect",
description: "连接直播服务器超时时间 毫秒",
type: "uint",
configType: "globalOnly",
advancedConfig: true,
defaultValue: "5 * 1000",
defaultValueDescription: "5000 (5秒)",
markdown: ""
},
{
name: "TimingDanmakuRetry",
description: "弹幕服务器重连时间间隔 毫秒",
type: "uint",
configType: "globalOnly",
advancedConfig: true,
defaultValue: "9 * 1000",
defaultValueDescription: "9000 (9秒)",
markdown: ""
},
{
name: "TimingWatchdogTimeout",
description: "最大允许未收到直播数据时间 毫秒",
type: "uint",
configType: "globalOnly",
advancedConfig: true,
defaultValue: "10 * 1000",
defaultValueDescription: "10000 (10秒)",
markdown: ""
},
{
name: "RecordDanmakuFlushInterval",
description: "触发刷新弹幕写入缓冲的个数",
type: "uint",
configType: "globalOnly",
advancedConfig: true,
defaultValue: "20",
xmlComment: "触发 <see cref=\"System.Xml.XmlWriter.Flush\"/> 的弹幕个数",
markdown: ""
},
];

View File

@ -0,0 +1,10 @@
# 设置代码以及文档生成
用法
```sh
npm install
npx ts-node index.ts
npx ts-node index.ts code
npx ts-node index.ts doc
```

View File

@ -0,0 +1,102 @@
import { ConfigEntry, ConfigEntryType } from "../types"
import builderCore from "./codeCore"
import builderCli from "./codeCli"
import builderSchema from "./codeSchema"
import builderWeb from "./codeWeb"
import { data } from "../data"
import { resolve } from "path"
import { writeFileSync } from "fs"
import { spawn } from "child_process"
import { stderr, stdout } from "process"
interface SectionInfoMap {
[key: string]: SectionInfo
}
interface SectionInfo {
readonly path: string
readonly format: boolean,
readonly header: boolean,
readonly build: ((data: ConfigEntry[]) => string)
}
const map: SectionInfoMap = {
core: {
path: './BililiveRecorder.Core/Config/V2/Config.gen.cs',
format: true,
header: true,
build: builderCore
},
cli: {
path: './BililiveRecorder.Cli/Configure/ConfigInstructions.gen.cs',
format: true,
header: true,
build: builderCli
},
web_is_disabled_for_now: {
path: './BililiveRecorder.Web.Schemas/Types/Config.gen.cs',
format: true,
header: true,
build: builderWeb
},
schema: {
path: './configV2.schema.json',
format: false,
header: false,
build: builderSchema
}
}
const HEADER = `// ******************************
// GENERATED CODE, DO NOT EDIT MANUALLY.
// SEE /config_gen/README.md
// ******************************\n\n`
export default function code(sections: string[]): void {
let formatList: string[] = [];
for (let i = 0; i < sections.length; i++) {
const info = map[sections[i]]
if (!info) continue;
let text = info.build(data)
if (info.header)
text = HEADER + text
const fullPath = resolve(__dirname, "../..", info.path)
writeFileSync(fullPath, text, { encoding: 'utf8' })
if (info.format)
formatList.push(info.path)
}
if (formatList.length > 0) {
console.log("[node] formatting...")
let format = spawn('dotnet',
[
'tool',
'run',
'dotnet-format',
'--',
'--include',
...formatList
],
{
cwd: resolve(__dirname, "../..")
})
format.stdout.on('data', function (data) {
stdout.write('[dotnet-format] ' + data.toString());
});
format.stderr.on('data', function (data) {
stderr.write('[dotnet-format] ' + data.toString());
});
format.on('exit', function (code) {
console.log('[node] format done, exit code: ' + code);
});
}
}

View File

@ -0,0 +1,50 @@
import { ConfigEntry, ConfigEntryType } from "../types"
import { trimEnd } from "../utils";
export default function (data: ConfigEntry[]): string {
let result = `using System.Collections.Generic;
using System.ComponentModel;
using BililiveRecorder.Core.Config.V2;
namespace BililiveRecorder.Cli.Configure
{`;
result += `
public enum GlobalConfigProperties
{
[Description("[grey]Exit[/]")]
Exit,
${data.filter(x => x.configType != 'roomOnly').map(x => x.name).join(",\n")}
}`;
result += `
public enum RoomConfigProperties
{
[Description("[grey]Exit[/]")]
Exit,
${data.filter(x => x.configType != 'globalOnly').map(x => x.name).join(",\n")}
}`;
result += `
public static class ConfigInstructions
{
public static Dictionary<GlobalConfigProperties, ConfigInstructionBase<GlobalConfig>> GlobalConfig = new();
public static Dictionary<RoomConfigProperties, ConfigInstructionBase<RoomConfig>> RoomConfig = new();
static ConfigInstructions()
{
${data
.filter(x => x.configType != 'roomOnly')
.map(r => `GlobalConfig.Add(GlobalConfigProperties.${r.name}, new ConfigInstruction<GlobalConfig, ${trimEnd(r.type, '?')}>(config => config.Has${r.name} = false, (config, value) => config.${r.name} = value) { Name = "${r.name}", CanBeOptional = true });`)
.join("\n")
}
${data.filter(x => x.configType != 'globalOnly').map(r => `RoomConfig.Add(RoomConfigProperties.${r.name}, new ConfigInstruction<RoomConfig, ${trimEnd(r.type, '?')}>(config => config.Has${r.name} = false, (config, value) => config.${r.name} = value) { Name = "${r.name}", CanBeOptional = ${r.configType != 'roomOnly'} });`).join("\n")}
}
}
`
result += `\n}\n`;
return result;
}

View File

@ -0,0 +1,65 @@
import { ConfigEntry, ConfigEntryType } from "../types"
import { trimEnd } from "../utils";
export default function (data: ConfigEntry[]): string {
let result = `using System.ComponentModel;
using HierarchicalPropertyDefault;
using Newtonsoft.Json;
#nullable enable
namespace BililiveRecorder.Core.Config.V2
{
`;
function write_property(r: ConfigEntry) {
result += `/// <summary>\n/// ${r.xmlComment ?? r.description}\n/// </summary>\n`;
result += `public ${r.type} ${r.name} { get => this.GetPropertyValue<${trimEnd(r.type, '?')}>(); set => this.SetPropertyValue(value); }\n`;
result += `public bool Has${r.name} { get => this.GetPropertyHasValue(nameof(this.${r.name})); set => this.SetPropertyHasValue<${trimEnd(r.type, '?')}>(value, nameof(this.${r.name})); }\n`;
result += `[JsonProperty(nameof(${r.name})), EditorBrowsable(EditorBrowsableState.Never)]\n`;
result += `public Optional<${r.type}> Optional${r.name} { get => this.GetPropertyValueOptional<${trimEnd(r.type, '?')}>(nameof(this.${r.name})); set => this.SetPropertyValueOptional(value, nameof(this.${r.name})); }\n\n`;
}
function write_readonly_property(r: ConfigEntry) {
result += `/// <summary>\n/// ${r.xmlComment ?? r.description}\n/// </summary>\n`;
result += `public ${r.type} ${r.name} => this.GetPropertyValue<${trimEnd(r.type, '?')}>();\n\n`;
}
{
result += "[JsonObject(MemberSerialization.OptIn)]\n";
result += "public sealed partial class RoomConfig : HierarchicalObject<GlobalConfig, RoomConfig>\n";
result += "{\n";
data.filter(x => x.configType != 'globalOnly').forEach(r => write_property(r));
data.filter(x => x.configType == 'globalOnly').forEach(r => write_readonly_property(r));
result += "}\n\n";
}
{
result += "[JsonObject(MemberSerialization.OptIn)]\n";
result += "public sealed partial class GlobalConfig : HierarchicalObject<DefaultConfig, GlobalConfig>\n";
result += "{\n";
data.filter(x => x.configType != 'roomOnly').forEach(r => write_property(r));
result += "}\n\n";
}
{
result += `public sealed partial class DefaultConfig
{
public static readonly DefaultConfig Instance = new DefaultConfig();
private DefaultConfig() {}\n\n`;
data
.filter(x => x.configType != 'roomOnly')
.forEach(r => {
result += `public ${trimEnd(r.type, '?')} ${r.name} => ${r.defaultValue};\n\n`;
});
result += "}\n\n";
}
result += `}\n`;
return result;
}

View File

@ -0,0 +1,110 @@
import { ConfigEntry, ConfigEntryType } from "../types"
function tryEvalValue(str: string) {
try {
return eval(str);
} catch {
return str;
}
}
const mapCSharpString = (text: string): string => text === 'string.Empty' ? '' : tryEvalValue(text.replace(/^@/, ''));
function mapTypeToJsonSchema(name: string, type: string, defaultValue: string) {
switch (type) {
case "RecordMode":
return { type: "integer", default: 0, enum: [0, 1], "description": "0: Standard\n1: Raw" };
case "CuttingMode":
return { type: "integer", default: 0, enum: [0, 1, 2], "description": "0: 禁用\n1: 根据时间切割\n2: 根据文件大小切割" };
case "uint":
return { type: "integer", minimum: 0, maximum: 4294967295, default: tryEvalValue(defaultValue) };
case "int":
return { type: "integer", minimum: -2147483648, maximum: 2147483647, default: tryEvalValue(defaultValue) };
case "bool":
return { type: "boolean", default: tryEvalValue(defaultValue) };
case "string":
case "string?":
if (name === 'Cookie') {
return { type: "string", pattern: "^(\S+=\S+;? ?)*$", maxLength: 4096, };
}
return { type: "string", default: mapCSharpString(defaultValue) };
default:
return { type, default: defaultValue };
}
}
function buildProperty(target: { [i: string]: any }, config: ConfigEntry) {
const typeObj = mapTypeToJsonSchema(config.name, config.type, config.defaultValue);
if (config.defaultValue === 'default')
delete typeObj['default'];
target[config.name] = {
description: config.description
+ '\n默认: ' + (!config.defaultValueDescription ? mapCSharpString(config.defaultValue) : config.defaultValueDescription),
markdownDescription: config.description
+ ' \n默认: `' + (!config.defaultValueDescription ? mapCSharpString(config.defaultValue) : config.defaultValueDescription)
+ ' `\n\n' + config.markdown,
type: "object",
additionalProperties: false,
properties: {
HasValue: {
type: "boolean",
default: true
},
Value: typeObj
}
};
}
export default function (data: ConfigEntry[]): string {
const sharedConfig = {};
const globalConfig = {};
const roomConfig = {};
data.filter(x => x.configType == 'room').forEach(v => buildProperty(sharedConfig, v));
data.filter(x => x.configType == 'roomOnly').forEach(v => buildProperty(roomConfig, v));
data.filter(x => x.configType == 'globalOnly').forEach(v => buildProperty(globalConfig, v));
const schema = {
"$comment": "GENERATED CODE, DO NOT EDIT MANUALLY.",
"$schema": "http://json-schema.org/schema",
"definitions": {
"global-config": {
"description": "全局设置",
"additionalProperties": false,
"properties": { ...globalConfig, ...sharedConfig }
},
"room-config": {
"description": "房间独立设置",
"additionalProperties": false,
"properties": { ...roomConfig, ...sharedConfig }
}
},
"type": "object",
"additionalProperties": false,
"required": [
"$schema",
"version"
],
"properties": {
"$schema": {
"type": "string",
"default": "https://raw.githubusercontent.com/Bililive/BililiveRecorder/dev-1.3/configV2.schema.json"
},
"version": {
"const": 2
},
"global": {
"$ref": "#/definitions/global-config"
},
"rooms": {
"type": "array",
"items": {
"$ref": "#/definitions/room-config"
}
}
}
}
return JSON.stringify(schema, null, 2)
}

View File

@ -0,0 +1,5 @@
import { ConfigEntry, ConfigEntryType } from "../types"
export default function (data: ConfigEntry[]): string {
return ""
}

View File

@ -0,0 +1,46 @@
import { ConfigEntry } from "../types"
import fs from "fs"
import { resolve } from "path"
import { data } from "../data";
import { trimEnd } from "../utils";
export default function doc(path: string): void {
if (!fs.statSync(resolve(path, '_config.yml'))) {
console.error('Check your path');
return;
}
if (!fs.statSync(resolve(path, 'index.html'))) {
console.error('Check your path');
return;
}
const targetPath = resolve(path, '_includes/generated_settings_list.md')
const text = buildMarkdown(data)
fs.writeFileSync(targetPath, text, { encoding: 'utf8' });
}
function buildMarkdown(data: ConfigEntry[]): string {
let result = '';
// 目录
result += "## 目录\n\n"
result += data.filter(x => !x.advancedConfig).map(x => `- [${x.description}](#${x.description})`).join('\n')
result += '\n\n'
// 一般设置项目列表
result += data.filter(x => !x.advancedConfig).map(x =>
`### ${x.description}
: \`${x.name}\`
: \`${trimEnd(x.type, '?')}\`
: \`${x.defaultValueDescription ?? x.defaultValue}\`
${x.markdown}
`).join('\n')
result += '\n\n'
return result;
}

View File

@ -0,0 +1,12 @@
import { ConfigEntry } from "../types"
export { default as code } from "./code"
export { default as doc } from "./doc"
export const core = function (data: Array<ConfigEntry>) {
}
export const cli = function (data: Array<ConfigEntry>) {
}
export const web = function (data: Array<ConfigEntry>) {
}
export const schema = function (data: Array<ConfigEntry>) {
}

46
config_gen/index.ts Normal file
View File

@ -0,0 +1,46 @@
import { spawn } from "child_process";
import { stdout, stderr } from "process";
import { writeFileSync } from "fs";
import { resolve, dirname } from "path";
import { fileURLToPath } from 'url';
import { data } from "./data"
import * as generators from "./generators"
const baseDirectory = __dirname
const argv = process.argv.slice(2)
switch (argv[0]) {
case "c":
case "code":
const availableSections = ["core", "cli", "web", "schema"];
let sections = argv.slice(1)
sections = sections.length == 0
? availableSections
: sections.filter(value => {
const r = availableSections.includes(value);
if (!r)
console.warn(`"${value}" is not a valid section name`)
return r;
})
if (sections.length == 0) {
console.error("Select valid sections to generate")
} else {
generators.code(sections);
}
break;
case "d":
case "doc":
if (typeof argv[1] === 'string') {
generators.doc(argv[1]);
} else {
console.error("Missing argument: <path to website> is required")
}
break;
default:
console.log(`Usage:
c, code [core, cli, web, schema]
d, doc <path to website>`);
break;
}

125
config_gen/package-lock.json generated Normal file
View File

@ -0,0 +1,125 @@
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"@cspotcode/source-map-consumer": {
"version": "0.8.0",
"resolved": "https://registry.nlark.com/@cspotcode/source-map-consumer/download/@cspotcode/source-map-consumer-0.8.0.tgz?cache=0&sync_timestamp=1628465044726&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40cspotcode%2Fsource-map-consumer%2Fdownload%2F%40cspotcode%2Fsource-map-consumer-0.8.0.tgz",
"integrity": "sha1-M79LeznBeIIWBvZpu8RHpqYpeGs=",
"dev": true
},
"@cspotcode/source-map-support": {
"version": "0.6.1",
"resolved": "https://registry.nlark.com/@cspotcode/source-map-support/download/@cspotcode/source-map-support-0.6.1.tgz?cache=0&sync_timestamp=1628465040948&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40cspotcode%2Fsource-map-support%2Fdownload%2F%40cspotcode%2Fsource-map-support-0.6.1.tgz",
"integrity": "sha1-EYUR8xbi6H7kKUdhho4lTT2keWA=",
"dev": true,
"requires": {
"@cspotcode/source-map-consumer": "0.8.0"
}
},
"@tsconfig/node10": {
"version": "1.0.8",
"resolved": "https://registry.nlark.com/@tsconfig/node10/download/@tsconfig/node10-1.0.8.tgz",
"integrity": "sha1-weToDW+WT77LM1nEO9SLQPfK2tk=",
"dev": true
},
"@tsconfig/node12": {
"version": "1.0.9",
"resolved": "https://registry.nlark.com/@tsconfig/node12/download/@tsconfig/node12-1.0.9.tgz",
"integrity": "sha1-YsH23uLr2a6tgNw6+laBDljhoEw=",
"dev": true
},
"@tsconfig/node14": {
"version": "1.0.1",
"resolved": "https://registry.nlark.com/@tsconfig/node14/download/@tsconfig/node14-1.0.1.tgz",
"integrity": "sha1-lfLRZ/+5uNIGiwsjUwL6/U33EfI=",
"dev": true
},
"@tsconfig/node16": {
"version": "1.0.2",
"resolved": "https://registry.nlark.com/@tsconfig/node16/download/@tsconfig/node16-1.0.2.tgz",
"integrity": "sha1-Qjx3h30Fadsg4fyAiFrEEYMUAQ4=",
"dev": true
},
"@types/node": {
"version": "16.6.1",
"resolved": "https://registry.nlark.com/@types/node/download/@types/node-16.6.1.tgz?cache=0&sync_timestamp=1628800447602&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-16.6.1.tgz",
"integrity": "sha1-ruYse5ZvVfxmx7bfodWNsqYW2mE=",
"dev": true
},
"acorn": {
"version": "8.4.1",
"resolved": "https://registry.nlark.com/acorn/download/acorn-8.4.1.tgz",
"integrity": "sha1-VsNiUfx8q8cJatwY8Fr+gUMhoow=",
"dev": true
},
"acorn-walk": {
"version": "8.1.1",
"resolved": "https://registry.nlark.com/acorn-walk/download/acorn-walk-8.1.1.tgz",
"integrity": "sha1-Pdq3+E5KfiMT9sQUxbfayF9OPrw=",
"dev": true
},
"arg": {
"version": "4.1.3",
"resolved": "https://registry.npm.taobao.org/arg/download/arg-4.1.3.tgz",
"integrity": "sha1-Jp/HrVuOQstjyJbVZmAXJhwUQIk=",
"dev": true
},
"create-require": {
"version": "1.1.1",
"resolved": "https://registry.npm.taobao.org/create-require/download/create-require-1.1.1.tgz",
"integrity": "sha1-wdfo8eX2z8n/ZfnNNS03NIdWwzM=",
"dev": true
},
"diff": {
"version": "4.0.2",
"resolved": "https://registry.npm.taobao.org/diff/download/diff-4.0.2.tgz",
"integrity": "sha1-YPOuy4nV+uUgwRqhnvwruYKq3n0=",
"dev": true
},
"make-error": {
"version": "1.3.6",
"resolved": "https://registry.npm.taobao.org/make-error/download/make-error-1.3.6.tgz?cache=0&sync_timestamp=1582105487630&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmake-error%2Fdownload%2Fmake-error-1.3.6.tgz",
"integrity": "sha1-LrLjfqm2fEiR9oShOUeZr0hM96I=",
"dev": true
},
"ts-node": {
"version": "10.2.0",
"resolved": "https://registry.nlark.com/ts-node/download/ts-node-10.2.0.tgz?cache=0&sync_timestamp=1628464846072&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fts-node%2Fdownload%2Fts-node-10.2.0.tgz",
"integrity": "sha1-8eiCSaAOJqqV6ak8UPcCQaihxLs=",
"dev": true,
"requires": {
"@cspotcode/source-map-support": "0.6.1",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"yn": "3.1.1"
}
},
"tslib": {
"version": "2.3.1",
"resolved": "https://registry.nlark.com/tslib/download/tslib-2.3.1.tgz",
"integrity": "sha1-6KM1rdXOrlGqJh0ypJAVjvBC7wE=",
"dev": true
},
"typescript": {
"version": "4.3.5",
"resolved": "https://registry.nlark.com/typescript/download/typescript-4.3.5.tgz",
"integrity": "sha1-TRw3zBbok5c8RaBohrcRMjTxGfQ=",
"dev": true
},
"yn": {
"version": "3.1.1",
"resolved": "https://registry.npm.taobao.org/yn/download/yn-3.1.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fyn%2Fdownload%2Fyn-3.1.1.tgz",
"integrity": "sha1-HodAGgnXZ8HV6rJqbkwYUYLS61A=",
"dev": true
}
}
}

12
config_gen/package.json Normal file
View File

@ -0,0 +1,12 @@
{
"scripts": {
"build": "ts-node index.ts"
},
"devDependencies": {
"@types/node": "^16.6.1",
"ts-node": "^10.2.0",
"tslib": "^2.3.1",
"typescript": "^4.3.5"
},
"dependencies": {}
}

29
config_gen/types.ts Normal file
View File

@ -0,0 +1,29 @@
/** 设置类型 */
export type ConfigEntryType =
/** 仅全局设置 */
"globalOnly"
/** 可单独房间设置或全局设置 */
| "room"
/** 只能单独房间设置 */
| "roomOnly"
export interface ConfigEntry {
/** 名字 */
readonly name: string,
/** 说明 */
readonly description: string,
/** 代码类型 */
readonly type: string,
/** 设置类型 */
readonly configType: ConfigEntryType
/** 是否为高级设置(隐藏设置),默认为 false */
readonly advancedConfig?: boolean,
/** 默认值 */
readonly defaultValue: string,
/** 文档显示用默认值,默认使用 defaultValue */
readonly defaultValueDescription?: string,
/** XML 注释,默认使用 description */
readonly xmlComment?: string,
/** Markdown 格式的说明文档 */
readonly markdown: string,
}

5
config_gen/utils.ts Normal file
View File

@ -0,0 +1,5 @@
export function trimEnd(text: string, trimChar: string): string {
return text.slice(-1) === trimChar
? text.slice(0, -1)
: text;
}