From 5f96eb539375bf511090985b7c642b028f709beb Mon Sep 17 00:00:00 2001 From: Genteure Date: Fri, 13 Aug 2021 21:03:21 +0800 Subject: [PATCH] New config gen --- .../Configure/ConfigInstructions.gen.cs | 52 +-- BililiveRecorder.Core/Config/V2/Config.gen.cs | 302 +++++++++--------- configV2.schema.json | 198 +++++++----- config_gen/data.ts | 208 ++++++++++++ config_gen/generators/README.md | 10 + config_gen/generators/code.ts | 102 ++++++ config_gen/generators/codeCli.ts | 50 +++ config_gen/generators/codeCore.ts | 65 ++++ config_gen/generators/codeSchema.ts | 110 +++++++ config_gen/generators/codeWeb.ts | 5 + config_gen/generators/doc.ts | 46 +++ config_gen/generators/index.ts | 12 + config_gen/index.ts | 46 +++ config_gen/package-lock.json | 125 ++++++++ config_gen/package.json | 12 + config_gen/types.ts | 29 ++ config_gen/utils.ts | 5 + 17 files changed, 1117 insertions(+), 260 deletions(-) create mode 100644 config_gen/data.ts create mode 100644 config_gen/generators/README.md create mode 100644 config_gen/generators/code.ts create mode 100644 config_gen/generators/codeCli.ts create mode 100644 config_gen/generators/codeCore.ts create mode 100644 config_gen/generators/codeSchema.ts create mode 100644 config_gen/generators/codeWeb.ts create mode 100644 config_gen/generators/doc.ts create mode 100644 config_gen/generators/index.ts create mode 100644 config_gen/index.ts create mode 100644 config_gen/package-lock.json create mode 100644 config_gen/package.json create mode 100644 config_gen/types.ts create mode 100644 config_gen/utils.ts diff --git a/BililiveRecorder.Cli/Configure/ConfigInstructions.gen.cs b/BililiveRecorder.Cli/Configure/ConfigInstructions.gen.cs index 51310266..b178344f 100644 --- a/BililiveRecorder.Cli/Configure/ConfigInstructions.gen.cs +++ b/BililiveRecorder.Cli/Configure/ConfigInstructions.gen.cs @@ -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 @@ public enum GlobalConfigProperties { [Description("[grey]Exit[/]")] Exit, - TimingStreamRetry, - TimingStreamConnect, - TimingDanmakuRetry, - TimingCheckInterval, - TimingWatchdogTimeout, - RecordDanmakuFlushInterval, - Cookie, - WebHookUrls, - WebHookUrlsV2, - LiveApiHost, - RecordFilenameFormat, - WpfShowTitleAndArea, RecordMode, CuttingMode, CuttingNumber, @@ -33,7 +21,19 @@ public enum GlobalConfigProperties RecordDanmakuSuperChat, RecordDanmakuGift, RecordDanmakuGuard, - RecordingQuality + RecordingQuality, + RecordFilenameFormat, + WebHookUrls, + WebHookUrlsV2, + WpfShowTitleAndArea, + Cookie, + LiveApiHost, + TimingCheckInterval, + TimingStreamRetry, + TimingStreamConnect, + TimingDanmakuRetry, + TimingWatchdogTimeout, + RecordDanmakuFlushInterval } public enum RoomConfigProperties { @@ -58,18 +58,6 @@ public static class ConfigInstructions static ConfigInstructions() { - GlobalConfig.Add(GlobalConfigProperties.TimingStreamRetry, new ConfigInstruction(config => config.HasTimingStreamRetry = false, (config, value) => config.TimingStreamRetry = value) { Name = "TimingStreamRetry", CanBeOptional = true }); - GlobalConfig.Add(GlobalConfigProperties.TimingStreamConnect, new ConfigInstruction(config => config.HasTimingStreamConnect = false, (config, value) => config.TimingStreamConnect = value) { Name = "TimingStreamConnect", CanBeOptional = true }); - GlobalConfig.Add(GlobalConfigProperties.TimingDanmakuRetry, new ConfigInstruction(config => config.HasTimingDanmakuRetry = false, (config, value) => config.TimingDanmakuRetry = value) { Name = "TimingDanmakuRetry", CanBeOptional = true }); - GlobalConfig.Add(GlobalConfigProperties.TimingCheckInterval, new ConfigInstruction(config => config.HasTimingCheckInterval = false, (config, value) => config.TimingCheckInterval = value) { Name = "TimingCheckInterval", CanBeOptional = true }); - GlobalConfig.Add(GlobalConfigProperties.TimingWatchdogTimeout, new ConfigInstruction(config => config.HasTimingWatchdogTimeout = false, (config, value) => config.TimingWatchdogTimeout = value) { Name = "TimingWatchdogTimeout", CanBeOptional = true }); - GlobalConfig.Add(GlobalConfigProperties.RecordDanmakuFlushInterval, new ConfigInstruction(config => config.HasRecordDanmakuFlushInterval = false, (config, value) => config.RecordDanmakuFlushInterval = value) { Name = "RecordDanmakuFlushInterval", CanBeOptional = true }); - GlobalConfig.Add(GlobalConfigProperties.Cookie, new ConfigInstruction(config => config.HasCookie = false, (config, value) => config.Cookie = value) { Name = "Cookie", CanBeOptional = true }); - GlobalConfig.Add(GlobalConfigProperties.WebHookUrls, new ConfigInstruction(config => config.HasWebHookUrls = false, (config, value) => config.WebHookUrls = value) { Name = "WebHookUrls", CanBeOptional = true }); - GlobalConfig.Add(GlobalConfigProperties.WebHookUrlsV2, new ConfigInstruction(config => config.HasWebHookUrlsV2 = false, (config, value) => config.WebHookUrlsV2 = value) { Name = "WebHookUrlsV2", CanBeOptional = true }); - GlobalConfig.Add(GlobalConfigProperties.LiveApiHost, new ConfigInstruction(config => config.HasLiveApiHost = false, (config, value) => config.LiveApiHost = value) { Name = "LiveApiHost", CanBeOptional = true }); - GlobalConfig.Add(GlobalConfigProperties.RecordFilenameFormat, new ConfigInstruction(config => config.HasRecordFilenameFormat = false, (config, value) => config.RecordFilenameFormat = value) { Name = "RecordFilenameFormat", CanBeOptional = true }); - GlobalConfig.Add(GlobalConfigProperties.WpfShowTitleAndArea, new ConfigInstruction(config => config.HasWpfShowTitleAndArea = false, (config, value) => config.WpfShowTitleAndArea = value) { Name = "WpfShowTitleAndArea", CanBeOptional = true }); GlobalConfig.Add(GlobalConfigProperties.RecordMode, new ConfigInstruction(config => config.HasRecordMode = false, (config, value) => config.RecordMode = value) { Name = "RecordMode", CanBeOptional = true }); GlobalConfig.Add(GlobalConfigProperties.CuttingMode, new ConfigInstruction(config => config.HasCuttingMode = false, (config, value) => config.CuttingMode = value) { Name = "CuttingMode", CanBeOptional = true }); GlobalConfig.Add(GlobalConfigProperties.CuttingNumber, new ConfigInstruction(config => config.HasCuttingNumber = false, (config, value) => config.CuttingNumber = value) { Name = "CuttingNumber", CanBeOptional = true }); @@ -79,6 +67,18 @@ static ConfigInstructions() GlobalConfig.Add(GlobalConfigProperties.RecordDanmakuGift, new ConfigInstruction(config => config.HasRecordDanmakuGift = false, (config, value) => config.RecordDanmakuGift = value) { Name = "RecordDanmakuGift", CanBeOptional = true }); GlobalConfig.Add(GlobalConfigProperties.RecordDanmakuGuard, new ConfigInstruction(config => config.HasRecordDanmakuGuard = false, (config, value) => config.RecordDanmakuGuard = value) { Name = "RecordDanmakuGuard", CanBeOptional = true }); GlobalConfig.Add(GlobalConfigProperties.RecordingQuality, new ConfigInstruction(config => config.HasRecordingQuality = false, (config, value) => config.RecordingQuality = value) { Name = "RecordingQuality", CanBeOptional = true }); + GlobalConfig.Add(GlobalConfigProperties.RecordFilenameFormat, new ConfigInstruction(config => config.HasRecordFilenameFormat = false, (config, value) => config.RecordFilenameFormat = value) { Name = "RecordFilenameFormat", CanBeOptional = true }); + GlobalConfig.Add(GlobalConfigProperties.WebHookUrls, new ConfigInstruction(config => config.HasWebHookUrls = false, (config, value) => config.WebHookUrls = value) { Name = "WebHookUrls", CanBeOptional = true }); + GlobalConfig.Add(GlobalConfigProperties.WebHookUrlsV2, new ConfigInstruction(config => config.HasWebHookUrlsV2 = false, (config, value) => config.WebHookUrlsV2 = value) { Name = "WebHookUrlsV2", CanBeOptional = true }); + GlobalConfig.Add(GlobalConfigProperties.WpfShowTitleAndArea, new ConfigInstruction(config => config.HasWpfShowTitleAndArea = false, (config, value) => config.WpfShowTitleAndArea = value) { Name = "WpfShowTitleAndArea", CanBeOptional = true }); + GlobalConfig.Add(GlobalConfigProperties.Cookie, new ConfigInstruction(config => config.HasCookie = false, (config, value) => config.Cookie = value) { Name = "Cookie", CanBeOptional = true }); + GlobalConfig.Add(GlobalConfigProperties.LiveApiHost, new ConfigInstruction(config => config.HasLiveApiHost = false, (config, value) => config.LiveApiHost = value) { Name = "LiveApiHost", CanBeOptional = true }); + GlobalConfig.Add(GlobalConfigProperties.TimingCheckInterval, new ConfigInstruction(config => config.HasTimingCheckInterval = false, (config, value) => config.TimingCheckInterval = value) { Name = "TimingCheckInterval", CanBeOptional = true }); + GlobalConfig.Add(GlobalConfigProperties.TimingStreamRetry, new ConfigInstruction(config => config.HasTimingStreamRetry = false, (config, value) => config.TimingStreamRetry = value) { Name = "TimingStreamRetry", CanBeOptional = true }); + GlobalConfig.Add(GlobalConfigProperties.TimingStreamConnect, new ConfigInstruction(config => config.HasTimingStreamConnect = false, (config, value) => config.TimingStreamConnect = value) { Name = "TimingStreamConnect", CanBeOptional = true }); + GlobalConfig.Add(GlobalConfigProperties.TimingDanmakuRetry, new ConfigInstruction(config => config.HasTimingDanmakuRetry = false, (config, value) => config.TimingDanmakuRetry = value) { Name = "TimingDanmakuRetry", CanBeOptional = true }); + GlobalConfig.Add(GlobalConfigProperties.TimingWatchdogTimeout, new ConfigInstruction(config => config.HasTimingWatchdogTimeout = false, (config, value) => config.TimingWatchdogTimeout = value) { Name = "TimingWatchdogTimeout", CanBeOptional = true }); + GlobalConfig.Add(GlobalConfigProperties.RecordDanmakuFlushInterval, new ConfigInstruction(config => config.HasRecordDanmakuFlushInterval = false, (config, value) => config.RecordDanmakuFlushInterval = value) { Name = "RecordDanmakuFlushInterval", CanBeOptional = true }); RoomConfig.Add(RoomConfigProperties.RoomId, new ConfigInstruction(config => config.HasRoomId = false, (config, value) => config.RoomId = value) { Name = "RoomId", CanBeOptional = false }); RoomConfig.Add(RoomConfigProperties.AutoRecord, new ConfigInstruction(config => config.HasAutoRecord = false, (config, value) => config.AutoRecord = value) { Name = "AutoRecord", CanBeOptional = false }); diff --git a/BililiveRecorder.Core/Config/V2/Config.gen.cs b/BililiveRecorder.Core/Config/V2/Config.gen.cs index f33bb794..9c3132d8 100644 --- a/BililiveRecorder.Core/Config/V2/Config.gen.cs +++ b/BililiveRecorder.Core/Config/V2/Config.gen.cs @@ -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 @@ public sealed partial class RoomConfig : HierarchicalObject OptionalRoomId { get => this.GetPropertyValueOptional(nameof(this.RoomId)); set => this.SetPropertyValueOptional(value, nameof(this.RoomId)); } /// - /// 是否启用自动录制 + /// 自动录制 /// public bool AutoRecord { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } public bool HasAutoRecord { get => this.GetPropertyHasValue(nameof(this.AutoRecord)); set => this.SetPropertyHasValue(value, nameof(this.AutoRecord)); } @@ -38,7 +38,7 @@ public sealed partial class RoomConfig : HierarchicalObject OptionalRecordMode { get => this.GetPropertyValueOptional(nameof(this.RecordMode)); set => this.SetPropertyValueOptional(value, nameof(this.RecordMode)); } /// - /// 录制文件自动切割模式 + /// 自动分段模式 /// public CuttingMode CuttingMode { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } public bool HasCuttingMode { get => this.GetPropertyHasValue(nameof(this.CuttingMode)); set => this.SetPropertyHasValue(value, nameof(this.CuttingMode)); } @@ -46,7 +46,7 @@ public sealed partial class RoomConfig : HierarchicalObject OptionalCuttingMode { get => this.GetPropertyValueOptional(nameof(this.CuttingMode)); set => this.SetPropertyValueOptional(value, nameof(this.CuttingMode)); } /// - /// 录制文件自动切割数值(分钟/MiB) + /// 自动分段数值 /// public uint CuttingNumber { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } public bool HasCuttingNumber { get => this.GetPropertyHasValue(nameof(this.CuttingNumber)); set => this.SetPropertyHasValue(value, nameof(this.CuttingNumber)); } @@ -54,7 +54,7 @@ public sealed partial class RoomConfig : HierarchicalObject OptionalCuttingNumber { get => this.GetPropertyValueOptional(nameof(this.CuttingNumber)); set => this.SetPropertyValueOptional(value, nameof(this.CuttingNumber)); } /// - /// 是否同时录制弹幕 + /// 弹幕录制 /// public bool RecordDanmaku { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } public bool HasRecordDanmaku { get => this.GetPropertyHasValue(nameof(this.RecordDanmaku)); set => this.SetPropertyHasValue(value, nameof(this.RecordDanmaku)); } @@ -62,7 +62,7 @@ public sealed partial class RoomConfig : HierarchicalObject OptionalRecordDanmaku { get => this.GetPropertyValueOptional(nameof(this.RecordDanmaku)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmaku)); } /// - /// 是否记录弹幕原始数据 + /// 弹幕录制-原始数据 /// public bool RecordDanmakuRaw { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } public bool HasRecordDanmakuRaw { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuRaw)); set => this.SetPropertyHasValue(value, nameof(this.RecordDanmakuRaw)); } @@ -70,7 +70,7 @@ public sealed partial class RoomConfig : HierarchicalObject OptionalRecordDanmakuRaw { get => this.GetPropertyValueOptional(nameof(this.RecordDanmakuRaw)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuRaw)); } /// - /// 是否同时录制 SuperChat + /// 弹幕录制-SuperChat /// public bool RecordDanmakuSuperChat { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } public bool HasRecordDanmakuSuperChat { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuSuperChat)); set => this.SetPropertyHasValue(value, nameof(this.RecordDanmakuSuperChat)); } @@ -78,7 +78,7 @@ public sealed partial class RoomConfig : HierarchicalObject OptionalRecordDanmakuSuperChat { get => this.GetPropertyValueOptional(nameof(this.RecordDanmakuSuperChat)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuSuperChat)); } /// - /// 是否同时录制 礼物 + /// 弹幕录制-礼物 /// public bool RecordDanmakuGift { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } public bool HasRecordDanmakuGift { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuGift)); set => this.SetPropertyHasValue(value, nameof(this.RecordDanmakuGift)); } @@ -86,7 +86,7 @@ public sealed partial class RoomConfig : HierarchicalObject OptionalRecordDanmakuGift { get => this.GetPropertyValueOptional(nameof(this.RecordDanmakuGift)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuGift)); } /// - /// 是否同时录制 上船 + /// 弹幕录制-上船 /// public bool RecordDanmakuGuard { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } public bool HasRecordDanmakuGuard { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuGuard)); set => this.SetPropertyHasValue(value, nameof(this.RecordDanmakuGuard)); } @@ -94,7 +94,7 @@ public sealed partial class RoomConfig : HierarchicalObject OptionalRecordDanmakuGuard { get => this.GetPropertyValueOptional(nameof(this.RecordDanmakuGuard)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuGuard)); } /// - /// 录制的直播画质 qn 值,逗号分割,靠前的优先 + /// 直播画质 /// public string? RecordingQuality { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } public bool HasRecordingQuality { get => this.GetPropertyHasValue(nameof(this.RecordingQuality)); set => this.SetPropertyHasValue(value, nameof(this.RecordingQuality)); } @@ -102,166 +102,70 @@ public sealed partial class RoomConfig : HierarchicalObject OptionalRecordingQuality { get => this.GetPropertyValueOptional(nameof(this.RecordingQuality)); set => this.SetPropertyValueOptional(value, nameof(this.RecordingQuality)); } /// - /// 录制断开重连时间间隔 毫秒 - /// - public uint TimingStreamRetry => this.GetPropertyValue(); - - /// - /// 连接直播服务器超时时间 毫秒 - /// - public uint TimingStreamConnect => this.GetPropertyValue(); - - /// - /// 弹幕服务器重连时间间隔 毫秒 + /// 录制文件名格式 /// - public uint TimingDanmakuRetry => this.GetPropertyValue(); + public string? RecordFilenameFormat => this.GetPropertyValue(); /// - /// HTTP API 检查时间间隔 秒 + /// 录制文件写入结束 Webhook 地址 每行一个 /// - public uint TimingCheckInterval => this.GetPropertyValue(); + public string? WebHookUrls => this.GetPropertyValue(); /// - /// 最大未收到新直播数据时间 毫秒 + /// Webhook v2 地址 每行一个 /// - public uint TimingWatchdogTimeout => this.GetPropertyValue(); + public string? WebHookUrlsV2 => this.GetPropertyValue(); /// - /// 触发 的弹幕个数 + /// 在界面显示标题和分区 /// - public uint RecordDanmakuFlushInterval => this.GetPropertyValue(); + public bool WpfShowTitleAndArea => this.GetPropertyValue(); /// /// 请求 API 时使用的 Cookie /// public string? Cookie => this.GetPropertyValue(); - /// - /// 录制文件写入结束 Webhook 地址 每行一个 - /// - public string? WebHookUrls => this.GetPropertyValue(); - - /// - /// Webhook v2 地址 每行一个 - /// - public string? WebHookUrlsV2 => this.GetPropertyValue(); - /// /// 替换 api.live.bilibili.com 服务器为其他反代,可以支持在云服务器上录制 /// public string? LiveApiHost => this.GetPropertyValue(); /// - /// 录制文件名模板 - /// - public string? RecordFilenameFormat => this.GetPropertyValue(); - - /// - /// 是否显示直播间标题和分区 + /// HTTP API 检查时间间隔 秒 /// - public bool WpfShowTitleAndArea => this.GetPropertyValue(); - - } + public uint TimingCheckInterval => this.GetPropertyValue(); - [JsonObject(MemberSerialization.OptIn)] - public sealed partial class GlobalConfig : HierarchicalObject - { /// /// 录制断开重连时间间隔 毫秒 /// - public uint TimingStreamRetry { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } - public bool HasTimingStreamRetry { get => this.GetPropertyHasValue(nameof(this.TimingStreamRetry)); set => this.SetPropertyHasValue(value, nameof(this.TimingStreamRetry)); } - [JsonProperty(nameof(TimingStreamRetry)), EditorBrowsable(EditorBrowsableState.Never)] - public Optional OptionalTimingStreamRetry { get => this.GetPropertyValueOptional(nameof(this.TimingStreamRetry)); set => this.SetPropertyValueOptional(value, nameof(this.TimingStreamRetry)); } + public uint TimingStreamRetry => this.GetPropertyValue(); /// /// 连接直播服务器超时时间 毫秒 /// - public uint TimingStreamConnect { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } - public bool HasTimingStreamConnect { get => this.GetPropertyHasValue(nameof(this.TimingStreamConnect)); set => this.SetPropertyHasValue(value, nameof(this.TimingStreamConnect)); } - [JsonProperty(nameof(TimingStreamConnect)), EditorBrowsable(EditorBrowsableState.Never)] - public Optional OptionalTimingStreamConnect { get => this.GetPropertyValueOptional(nameof(this.TimingStreamConnect)); set => this.SetPropertyValueOptional(value, nameof(this.TimingStreamConnect)); } + public uint TimingStreamConnect => this.GetPropertyValue(); /// /// 弹幕服务器重连时间间隔 毫秒 /// - public uint TimingDanmakuRetry { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } - public bool HasTimingDanmakuRetry { get => this.GetPropertyHasValue(nameof(this.TimingDanmakuRetry)); set => this.SetPropertyHasValue(value, nameof(this.TimingDanmakuRetry)); } - [JsonProperty(nameof(TimingDanmakuRetry)), EditorBrowsable(EditorBrowsableState.Never)] - public Optional OptionalTimingDanmakuRetry { get => this.GetPropertyValueOptional(nameof(this.TimingDanmakuRetry)); set => this.SetPropertyValueOptional(value, nameof(this.TimingDanmakuRetry)); } - - /// - /// HTTP API 检查时间间隔 秒 - /// - public uint TimingCheckInterval { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } - public bool HasTimingCheckInterval { get => this.GetPropertyHasValue(nameof(this.TimingCheckInterval)); set => this.SetPropertyHasValue(value, nameof(this.TimingCheckInterval)); } - [JsonProperty(nameof(TimingCheckInterval)), EditorBrowsable(EditorBrowsableState.Never)] - public Optional OptionalTimingCheckInterval { get => this.GetPropertyValueOptional(nameof(this.TimingCheckInterval)); set => this.SetPropertyValueOptional(value, nameof(this.TimingCheckInterval)); } + public uint TimingDanmakuRetry => this.GetPropertyValue(); /// - /// 最大未收到新直播数据时间 毫秒 + /// 最大允许未收到直播数据时间 毫秒 /// - public uint TimingWatchdogTimeout { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } - public bool HasTimingWatchdogTimeout { get => this.GetPropertyHasValue(nameof(this.TimingWatchdogTimeout)); set => this.SetPropertyHasValue(value, nameof(this.TimingWatchdogTimeout)); } - [JsonProperty(nameof(TimingWatchdogTimeout)), EditorBrowsable(EditorBrowsableState.Never)] - public Optional OptionalTimingWatchdogTimeout { get => this.GetPropertyValueOptional(nameof(this.TimingWatchdogTimeout)); set => this.SetPropertyValueOptional(value, nameof(this.TimingWatchdogTimeout)); } + public uint TimingWatchdogTimeout => this.GetPropertyValue(); /// /// 触发 的弹幕个数 /// - public uint RecordDanmakuFlushInterval { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } - public bool HasRecordDanmakuFlushInterval { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuFlushInterval)); set => this.SetPropertyHasValue(value, nameof(this.RecordDanmakuFlushInterval)); } - [JsonProperty(nameof(RecordDanmakuFlushInterval)), EditorBrowsable(EditorBrowsableState.Never)] - public Optional OptionalRecordDanmakuFlushInterval { get => this.GetPropertyValueOptional(nameof(this.RecordDanmakuFlushInterval)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuFlushInterval)); } - - /// - /// 请求 API 时使用的 Cookie - /// - public string? Cookie { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } - public bool HasCookie { get => this.GetPropertyHasValue(nameof(this.Cookie)); set => this.SetPropertyHasValue(value, nameof(this.Cookie)); } - [JsonProperty(nameof(Cookie)), EditorBrowsable(EditorBrowsableState.Never)] - public Optional OptionalCookie { get => this.GetPropertyValueOptional(nameof(this.Cookie)); set => this.SetPropertyValueOptional(value, nameof(this.Cookie)); } - - /// - /// 录制文件写入结束 Webhook 地址 每行一个 - /// - public string? WebHookUrls { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } - public bool HasWebHookUrls { get => this.GetPropertyHasValue(nameof(this.WebHookUrls)); set => this.SetPropertyHasValue(value, nameof(this.WebHookUrls)); } - [JsonProperty(nameof(WebHookUrls)), EditorBrowsable(EditorBrowsableState.Never)] - public Optional OptionalWebHookUrls { get => this.GetPropertyValueOptional(nameof(this.WebHookUrls)); set => this.SetPropertyValueOptional(value, nameof(this.WebHookUrls)); } - - /// - /// Webhook v2 地址 每行一个 - /// - public string? WebHookUrlsV2 { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } - public bool HasWebHookUrlsV2 { get => this.GetPropertyHasValue(nameof(this.WebHookUrlsV2)); set => this.SetPropertyHasValue(value, nameof(this.WebHookUrlsV2)); } - [JsonProperty(nameof(WebHookUrlsV2)), EditorBrowsable(EditorBrowsableState.Never)] - public Optional OptionalWebHookUrlsV2 { get => this.GetPropertyValueOptional(nameof(this.WebHookUrlsV2)); set => this.SetPropertyValueOptional(value, nameof(this.WebHookUrlsV2)); } - - /// - /// 替换 api.live.bilibili.com 服务器为其他反代,可以支持在云服务器上录制 - /// - public string? LiveApiHost { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } - public bool HasLiveApiHost { get => this.GetPropertyHasValue(nameof(this.LiveApiHost)); set => this.SetPropertyHasValue(value, nameof(this.LiveApiHost)); } - [JsonProperty(nameof(LiveApiHost)), EditorBrowsable(EditorBrowsableState.Never)] - public Optional OptionalLiveApiHost { get => this.GetPropertyValueOptional(nameof(this.LiveApiHost)); set => this.SetPropertyValueOptional(value, nameof(this.LiveApiHost)); } - - /// - /// 录制文件名模板 - /// - public string? RecordFilenameFormat { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } - public bool HasRecordFilenameFormat { get => this.GetPropertyHasValue(nameof(this.RecordFilenameFormat)); set => this.SetPropertyHasValue(value, nameof(this.RecordFilenameFormat)); } - [JsonProperty(nameof(RecordFilenameFormat)), EditorBrowsable(EditorBrowsableState.Never)] - public Optional OptionalRecordFilenameFormat { get => this.GetPropertyValueOptional(nameof(this.RecordFilenameFormat)); set => this.SetPropertyValueOptional(value, nameof(this.RecordFilenameFormat)); } + public uint RecordDanmakuFlushInterval => this.GetPropertyValue(); - /// - /// 是否显示直播间标题和分区 - /// - public bool WpfShowTitleAndArea { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } - public bool HasWpfShowTitleAndArea { get => this.GetPropertyHasValue(nameof(this.WpfShowTitleAndArea)); set => this.SetPropertyHasValue(value, nameof(this.WpfShowTitleAndArea)); } - [JsonProperty(nameof(WpfShowTitleAndArea)), EditorBrowsable(EditorBrowsableState.Never)] - public Optional OptionalWpfShowTitleAndArea { get => this.GetPropertyValueOptional(nameof(this.WpfShowTitleAndArea)); set => this.SetPropertyValueOptional(value, nameof(this.WpfShowTitleAndArea)); } + } + [JsonObject(MemberSerialization.OptIn)] + public sealed partial class GlobalConfig : HierarchicalObject + { /// /// 录制模式 /// @@ -271,7 +175,7 @@ public sealed partial class GlobalConfig : HierarchicalObject OptionalRecordMode { get => this.GetPropertyValueOptional(nameof(this.RecordMode)); set => this.SetPropertyValueOptional(value, nameof(this.RecordMode)); } /// - /// 录制文件自动切割模式 + /// 自动分段模式 /// public CuttingMode CuttingMode { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } public bool HasCuttingMode { get => this.GetPropertyHasValue(nameof(this.CuttingMode)); set => this.SetPropertyHasValue(value, nameof(this.CuttingMode)); } @@ -279,7 +183,7 @@ public sealed partial class GlobalConfig : HierarchicalObject OptionalCuttingMode { get => this.GetPropertyValueOptional(nameof(this.CuttingMode)); set => this.SetPropertyValueOptional(value, nameof(this.CuttingMode)); } /// - /// 录制文件自动切割数值(分钟/MiB) + /// 自动分段数值 /// public uint CuttingNumber { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } public bool HasCuttingNumber { get => this.GetPropertyHasValue(nameof(this.CuttingNumber)); set => this.SetPropertyHasValue(value, nameof(this.CuttingNumber)); } @@ -287,7 +191,7 @@ public sealed partial class GlobalConfig : HierarchicalObject OptionalCuttingNumber { get => this.GetPropertyValueOptional(nameof(this.CuttingNumber)); set => this.SetPropertyValueOptional(value, nameof(this.CuttingNumber)); } /// - /// 是否同时录制弹幕 + /// 弹幕录制 /// public bool RecordDanmaku { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } public bool HasRecordDanmaku { get => this.GetPropertyHasValue(nameof(this.RecordDanmaku)); set => this.SetPropertyHasValue(value, nameof(this.RecordDanmaku)); } @@ -295,7 +199,7 @@ public sealed partial class GlobalConfig : HierarchicalObject OptionalRecordDanmaku { get => this.GetPropertyValueOptional(nameof(this.RecordDanmaku)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmaku)); } /// - /// 是否记录弹幕原始数据 + /// 弹幕录制-原始数据 /// public bool RecordDanmakuRaw { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } public bool HasRecordDanmakuRaw { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuRaw)); set => this.SetPropertyHasValue(value, nameof(this.RecordDanmakuRaw)); } @@ -303,7 +207,7 @@ public sealed partial class GlobalConfig : HierarchicalObject OptionalRecordDanmakuRaw { get => this.GetPropertyValueOptional(nameof(this.RecordDanmakuRaw)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuRaw)); } /// - /// 是否同时录制 SuperChat + /// 弹幕录制-SuperChat /// public bool RecordDanmakuSuperChat { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } public bool HasRecordDanmakuSuperChat { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuSuperChat)); set => this.SetPropertyHasValue(value, nameof(this.RecordDanmakuSuperChat)); } @@ -311,7 +215,7 @@ public sealed partial class GlobalConfig : HierarchicalObject OptionalRecordDanmakuSuperChat { get => this.GetPropertyValueOptional(nameof(this.RecordDanmakuSuperChat)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuSuperChat)); } /// - /// 是否同时录制 礼物 + /// 弹幕录制-礼物 /// public bool RecordDanmakuGift { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } public bool HasRecordDanmakuGift { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuGift)); set => this.SetPropertyHasValue(value, nameof(this.RecordDanmakuGift)); } @@ -319,7 +223,7 @@ public sealed partial class GlobalConfig : HierarchicalObject OptionalRecordDanmakuGift { get => this.GetPropertyValueOptional(nameof(this.RecordDanmakuGift)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuGift)); } /// - /// 是否同时录制 上船 + /// 弹幕录制-上船 /// public bool RecordDanmakuGuard { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } public bool HasRecordDanmakuGuard { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuGuard)); set => this.SetPropertyHasValue(value, nameof(this.RecordDanmakuGuard)); } @@ -327,43 +231,115 @@ public sealed partial class GlobalConfig : HierarchicalObject OptionalRecordDanmakuGuard { get => this.GetPropertyValueOptional(nameof(this.RecordDanmakuGuard)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuGuard)); } /// - /// 录制的直播画质 qn 值,逗号分割,靠前的优先 + /// 直播画质 /// public string? RecordingQuality { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } public bool HasRecordingQuality { get => this.GetPropertyHasValue(nameof(this.RecordingQuality)); set => this.SetPropertyHasValue(value, nameof(this.RecordingQuality)); } [JsonProperty(nameof(RecordingQuality)), EditorBrowsable(EditorBrowsableState.Never)] public Optional OptionalRecordingQuality { get => this.GetPropertyValueOptional(nameof(this.RecordingQuality)); set => this.SetPropertyValueOptional(value, nameof(this.RecordingQuality)); } - } + /// + /// 录制文件名格式 + /// + public string? RecordFilenameFormat { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } + public bool HasRecordFilenameFormat { get => this.GetPropertyHasValue(nameof(this.RecordFilenameFormat)); set => this.SetPropertyHasValue(value, nameof(this.RecordFilenameFormat)); } + [JsonProperty(nameof(RecordFilenameFormat)), EditorBrowsable(EditorBrowsableState.Never)] + public Optional OptionalRecordFilenameFormat { get => this.GetPropertyValueOptional(nameof(this.RecordFilenameFormat)); set => this.SetPropertyValueOptional(value, nameof(this.RecordFilenameFormat)); } - public sealed partial class DefaultConfig - { - public static readonly DefaultConfig Instance = new DefaultConfig(); - private DefaultConfig() { } + /// + /// 录制文件写入结束 Webhook 地址 每行一个 + /// + public string? WebHookUrls { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } + public bool HasWebHookUrls { get => this.GetPropertyHasValue(nameof(this.WebHookUrls)); set => this.SetPropertyHasValue(value, nameof(this.WebHookUrls)); } + [JsonProperty(nameof(WebHookUrls)), EditorBrowsable(EditorBrowsableState.Never)] + public Optional OptionalWebHookUrls { get => this.GetPropertyValueOptional(nameof(this.WebHookUrls)); set => this.SetPropertyValueOptional(value, nameof(this.WebHookUrls)); } - public uint TimingStreamRetry => 6 * 1000; + /// + /// Webhook v2 地址 每行一个 + /// + public string? WebHookUrlsV2 { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } + public bool HasWebHookUrlsV2 { get => this.GetPropertyHasValue(nameof(this.WebHookUrlsV2)); set => this.SetPropertyHasValue(value, nameof(this.WebHookUrlsV2)); } + [JsonProperty(nameof(WebHookUrlsV2)), EditorBrowsable(EditorBrowsableState.Never)] + public Optional OptionalWebHookUrlsV2 { get => this.GetPropertyValueOptional(nameof(this.WebHookUrlsV2)); set => this.SetPropertyValueOptional(value, nameof(this.WebHookUrlsV2)); } - public uint TimingStreamConnect => 5 * 1000; + /// + /// 在界面显示标题和分区 + /// + public bool WpfShowTitleAndArea { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } + public bool HasWpfShowTitleAndArea { get => this.GetPropertyHasValue(nameof(this.WpfShowTitleAndArea)); set => this.SetPropertyHasValue(value, nameof(this.WpfShowTitleAndArea)); } + [JsonProperty(nameof(WpfShowTitleAndArea)), EditorBrowsable(EditorBrowsableState.Never)] + public Optional OptionalWpfShowTitleAndArea { get => this.GetPropertyValueOptional(nameof(this.WpfShowTitleAndArea)); set => this.SetPropertyValueOptional(value, nameof(this.WpfShowTitleAndArea)); } - public uint TimingDanmakuRetry => 15 * 1000; + /// + /// 请求 API 时使用的 Cookie + /// + public string? Cookie { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } + public bool HasCookie { get => this.GetPropertyHasValue(nameof(this.Cookie)); set => this.SetPropertyHasValue(value, nameof(this.Cookie)); } + [JsonProperty(nameof(Cookie)), EditorBrowsable(EditorBrowsableState.Never)] + public Optional OptionalCookie { get => this.GetPropertyValueOptional(nameof(this.Cookie)); set => this.SetPropertyValueOptional(value, nameof(this.Cookie)); } - public uint TimingCheckInterval => 10 * 60; + /// + /// 替换 api.live.bilibili.com 服务器为其他反代,可以支持在云服务器上录制 + /// + public string? LiveApiHost { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } + public bool HasLiveApiHost { get => this.GetPropertyHasValue(nameof(this.LiveApiHost)); set => this.SetPropertyHasValue(value, nameof(this.LiveApiHost)); } + [JsonProperty(nameof(LiveApiHost)), EditorBrowsable(EditorBrowsableState.Never)] + public Optional OptionalLiveApiHost { get => this.GetPropertyValueOptional(nameof(this.LiveApiHost)); set => this.SetPropertyValueOptional(value, nameof(this.LiveApiHost)); } - public uint TimingWatchdogTimeout => 10 * 1000; + /// + /// HTTP API 检查时间间隔 秒 + /// + public uint TimingCheckInterval { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } + public bool HasTimingCheckInterval { get => this.GetPropertyHasValue(nameof(this.TimingCheckInterval)); set => this.SetPropertyHasValue(value, nameof(this.TimingCheckInterval)); } + [JsonProperty(nameof(TimingCheckInterval)), EditorBrowsable(EditorBrowsableState.Never)] + public Optional OptionalTimingCheckInterval { get => this.GetPropertyValueOptional(nameof(this.TimingCheckInterval)); set => this.SetPropertyValueOptional(value, nameof(this.TimingCheckInterval)); } - public uint RecordDanmakuFlushInterval => 20; + /// + /// 录制断开重连时间间隔 毫秒 + /// + public uint TimingStreamRetry { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } + public bool HasTimingStreamRetry { get => this.GetPropertyHasValue(nameof(this.TimingStreamRetry)); set => this.SetPropertyHasValue(value, nameof(this.TimingStreamRetry)); } + [JsonProperty(nameof(TimingStreamRetry)), EditorBrowsable(EditorBrowsableState.Never)] + public Optional OptionalTimingStreamRetry { get => this.GetPropertyValueOptional(nameof(this.TimingStreamRetry)); set => this.SetPropertyValueOptional(value, nameof(this.TimingStreamRetry)); } - public string Cookie => string.Empty; + /// + /// 连接直播服务器超时时间 毫秒 + /// + public uint TimingStreamConnect { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } + public bool HasTimingStreamConnect { get => this.GetPropertyHasValue(nameof(this.TimingStreamConnect)); set => this.SetPropertyHasValue(value, nameof(this.TimingStreamConnect)); } + [JsonProperty(nameof(TimingStreamConnect)), EditorBrowsable(EditorBrowsableState.Never)] + public Optional OptionalTimingStreamConnect { get => this.GetPropertyValueOptional(nameof(this.TimingStreamConnect)); set => this.SetPropertyValueOptional(value, nameof(this.TimingStreamConnect)); } - public string WebHookUrls => string.Empty; + /// + /// 弹幕服务器重连时间间隔 毫秒 + /// + public uint TimingDanmakuRetry { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } + public bool HasTimingDanmakuRetry { get => this.GetPropertyHasValue(nameof(this.TimingDanmakuRetry)); set => this.SetPropertyHasValue(value, nameof(this.TimingDanmakuRetry)); } + [JsonProperty(nameof(TimingDanmakuRetry)), EditorBrowsable(EditorBrowsableState.Never)] + public Optional OptionalTimingDanmakuRetry { get => this.GetPropertyValueOptional(nameof(this.TimingDanmakuRetry)); set => this.SetPropertyValueOptional(value, nameof(this.TimingDanmakuRetry)); } - public string WebHookUrlsV2 => string.Empty; + /// + /// 最大允许未收到直播数据时间 毫秒 + /// + public uint TimingWatchdogTimeout { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } + public bool HasTimingWatchdogTimeout { get => this.GetPropertyHasValue(nameof(this.TimingWatchdogTimeout)); set => this.SetPropertyHasValue(value, nameof(this.TimingWatchdogTimeout)); } + [JsonProperty(nameof(TimingWatchdogTimeout)), EditorBrowsable(EditorBrowsableState.Never)] + public Optional OptionalTimingWatchdogTimeout { get => this.GetPropertyValueOptional(nameof(this.TimingWatchdogTimeout)); set => this.SetPropertyValueOptional(value, nameof(this.TimingWatchdogTimeout)); } - public string LiveApiHost => "https://api.live.bilibili.com"; + /// + /// 触发 的弹幕个数 + /// + public uint RecordDanmakuFlushInterval { get => this.GetPropertyValue(); set => this.SetPropertyValue(value); } + public bool HasRecordDanmakuFlushInterval { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuFlushInterval)); set => this.SetPropertyHasValue(value, nameof(this.RecordDanmakuFlushInterval)); } + [JsonProperty(nameof(RecordDanmakuFlushInterval)), EditorBrowsable(EditorBrowsableState.Never)] + public Optional OptionalRecordDanmakuFlushInterval { get => this.GetPropertyValueOptional(nameof(this.RecordDanmakuFlushInterval)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuFlushInterval)); } - public string RecordFilenameFormat => @"{roomid}-{name}/录制-{roomid}-{date}-{time}-{ms}-{title}.flv"; + } - public bool WpfShowTitleAndArea => true; + public sealed partial class DefaultConfig + { + public static readonly DefaultConfig Instance = new DefaultConfig(); + private DefaultConfig() { } public RecordMode RecordMode => RecordMode.Standard; @@ -383,6 +359,30 @@ public sealed partial class DefaultConfig 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; + } } diff --git a/configV2.schema.json b/configV2.schema.json index bb058a07..2c929677 100644 --- a/configV2.schema.json +++ b/configV2.schema.json @@ -3,11 +3,12 @@ "$schema": "http://json-schema.org/schema", "definitions": { "global-config": { - "description": "全局配置", + "description": "全局设置", "additionalProperties": false, "properties": { - "TimingStreamRetry": { - "description": "录制断开重连时间间隔 毫秒", + "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": { @@ -16,15 +17,14 @@ "default": true }, "Value": { - "type": "integer", - "minimum": 0, - "maximum": 4294967295, - "default": 6000 + "type": "string", + "default": "{roomid}-{name}/录制-{roomid}-{date}-{time}-{ms}-{title}.flv" } } }, - "TimingStreamConnect": { - "description": "连接直播服务器超时时间 毫秒", + "WebHookUrls": { + "description": "WebhookV1\n默认: ", + "markdownDescription": "WebhookV1 \n默认: ` `\n\n", "type": "object", "additionalProperties": false, "properties": { @@ -33,15 +33,14 @@ "default": true }, "Value": { - "type": "integer", - "minimum": 0, - "maximum": 4294967295, - "default": 5000 + "type": "string", + "default": "" } } }, - "TimingDanmakuRetry": { - "description": "弹幕服务器重连时间间隔 毫秒", + "WebHookUrlsV2": { + "description": "WebhookV2\n默认: ", + "markdownDescription": "WebhookV2 \n默认: ` `\n\n", "type": "object", "additionalProperties": false, "properties": { @@ -50,15 +49,14 @@ "default": true }, "Value": { - "type": "integer", - "minimum": 0, - "maximum": 4294967295, - "default": 15000 + "type": "string", + "default": "" } } }, - "TimingCheckInterval": { - "description": "HTTP API 检查时间间隔 秒", + "WpfShowTitleAndArea": { + "description": "在界面显示标题和分区\n默认: true", + "markdownDescription": "在界面显示标题和分区 \n默认: `true `\n\n", "type": "object", "additionalProperties": false, "properties": { @@ -67,15 +65,14 @@ "default": true }, "Value": { - "type": "integer", - "minimum": 0, - "maximum": 4294967295, - "default": 600 + "type": "boolean", + "default": true } } }, - "TimingWatchdogTimeout": { - "description": "最大未收到新直播数据时间 毫秒", + "Cookie": { + "description": "请求 API 时使用的 Cookie\n默认: (空字符串)", + "markdownDescription": "请求 API 时使用的 Cookie \n默认: `(空字符串) `\n\n", "type": "object", "additionalProperties": false, "properties": { @@ -84,15 +81,15 @@ "default": true }, "Value": { - "type": "integer", - "minimum": 0, - "maximum": 4294967295, - "default": 10000 + "type": "string", + "pattern": "^(S+=S+;? ?)*$", + "maxLength": 4096 } } }, - "RecordDanmakuFlushInterval": { - "description": "触发 的弹幕个数", + "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": { @@ -101,15 +98,14 @@ "default": true }, "Value": { - "type": "integer", - "minimum": 0, - "maximum": 4294967295, - "default": 20 + "type": "string", + "default": "https://api.live.bilibili.com" } } }, - "Cookie": { - "description": "请求 API 时使用的 Cookie", + "TimingCheckInterval": { + "description": "HTTP API 检查时间间隔 秒\n默认: 600 (10分)", + "markdownDescription": "HTTP API 检查时间间隔 秒 \n默认: `600 (10分) `\n\n", "type": "object", "additionalProperties": false, "properties": { @@ -118,14 +114,16 @@ "default": true }, "Value": { - "type": "string", - "pattern": "^(S+=S+;? ?)*$", - "maxLength": 4096 + "type": "integer", + "minimum": 0, + "maximum": 4294967295, + "default": 600 } } }, - "WebHookUrls": { - "description": "录制文件写入结束 Webhook 地址 每行一个", + "TimingStreamRetry": { + "description": "录制断开重连时间间隔 毫秒\n默认: 6000 (6秒)", + "markdownDescription": "录制断开重连时间间隔 毫秒 \n默认: `6000 (6秒) `\n\n", "type": "object", "additionalProperties": false, "properties": { @@ -134,13 +132,16 @@ "default": true }, "Value": { - "type": "string", - "default": "" + "type": "integer", + "minimum": 0, + "maximum": 4294967295, + "default": 6000 } } }, - "WebHookUrlsV2": { - "description": "Webhook v2 地址 每行一个", + "TimingStreamConnect": { + "description": "连接直播服务器超时时间 毫秒\n默认: 5000 (5秒)", + "markdownDescription": "连接直播服务器超时时间 毫秒 \n默认: `5000 (5秒) `\n\n", "type": "object", "additionalProperties": false, "properties": { @@ -149,13 +150,16 @@ "default": true }, "Value": { - "type": "string", - "default": "" + "type": "integer", + "minimum": 0, + "maximum": 4294967295, + "default": 5000 } } }, - "LiveApiHost": { - "description": "替换 api.live.bilibili.com 服务器为其他反代,可以支持在云服务器上录制", + "TimingDanmakuRetry": { + "description": "弹幕服务器重连时间间隔 毫秒\n默认: 9000 (9秒)", + "markdownDescription": "弹幕服务器重连时间间隔 毫秒 \n默认: `9000 (9秒) `\n\n", "type": "object", "additionalProperties": false, "properties": { @@ -164,13 +168,16 @@ "default": true }, "Value": { - "type": "string", - "default": "https://api.live.bilibili.com" + "type": "integer", + "minimum": 0, + "maximum": 4294967295, + "default": 9000 } } }, - "RecordFilenameFormat": { - "description": "录制文件名模板", + "TimingWatchdogTimeout": { + "description": "最大允许未收到直播数据时间 毫秒\n默认: 10000 (10秒)", + "markdownDescription": "最大允许未收到直播数据时间 毫秒 \n默认: `10000 (10秒) `\n\n", "type": "object", "additionalProperties": false, "properties": { @@ -179,13 +186,16 @@ "default": true }, "Value": { - "type": "string", - "default": "{roomid}-{name}/录制-{roomid}-{date}-{time}-{ms}-{title}.flv" + "type": "integer", + "minimum": 0, + "maximum": 4294967295, + "default": 10000 } } }, - "WpfShowTitleAndArea": { - "description": "是否显示直播间标题和分区", + "RecordDanmakuFlushInterval": { + "description": "触发刷新弹幕写入缓冲的个数\n默认: 20", + "markdownDescription": "触发刷新弹幕写入缓冲的个数 \n默认: `20 `\n\n", "type": "object", "additionalProperties": false, "properties": { @@ -194,13 +204,16 @@ "default": true }, "Value": { - "type": "boolean", - "default": true + "type": "integer", + "minimum": 0, + "maximum": 4294967295, + "default": 20 } } }, "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": { diff --git a/config_gen/data.ts b/config_gen/data.ts new file mode 100644 index 00000000..0a7458f6 --- /dev/null +++ b/config_gen/data.ts @@ -0,0 +1,208 @@ +import { ConfigEntry, ConfigEntryType } from './types' + +export const data: Array = [ + { + 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: "触发 的弹幕个数", + markdown: "" + }, +]; diff --git a/config_gen/generators/README.md b/config_gen/generators/README.md new file mode 100644 index 00000000..861897d4 --- /dev/null +++ b/config_gen/generators/README.md @@ -0,0 +1,10 @@ +# 设置代码以及文档生成 + +用法 + +```sh +npm install +npx ts-node index.ts +npx ts-node index.ts code +npx ts-node index.ts doc +``` diff --git a/config_gen/generators/code.ts b/config_gen/generators/code.ts new file mode 100644 index 00000000..e0a1d248 --- /dev/null +++ b/config_gen/generators/code.ts @@ -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); + }); + } +} diff --git a/config_gen/generators/codeCli.ts b/config_gen/generators/codeCli.ts new file mode 100644 index 00000000..8520771f --- /dev/null +++ b/config_gen/generators/codeCli.ts @@ -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> GlobalConfig = new(); + public static Dictionary> RoomConfig = new(); + + static ConfigInstructions() + { + ${data + .filter(x => x.configType != 'roomOnly') + .map(r => `GlobalConfig.Add(GlobalConfigProperties.${r.name}, new ConfigInstruction(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(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; +} diff --git a/config_gen/generators/codeCore.ts b/config_gen/generators/codeCore.ts new file mode 100644 index 00000000..45337e7c --- /dev/null +++ b/config_gen/generators/codeCore.ts @@ -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 += `/// \n/// ${r.xmlComment ?? r.description}\n/// \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 += `/// \n/// ${r.xmlComment ?? r.description}\n/// \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\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\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; +} diff --git a/config_gen/generators/codeSchema.ts b/config_gen/generators/codeSchema.ts new file mode 100644 index 00000000..78a0436d --- /dev/null +++ b/config_gen/generators/codeSchema.ts @@ -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) +} diff --git a/config_gen/generators/codeWeb.ts b/config_gen/generators/codeWeb.ts new file mode 100644 index 00000000..6400d23c --- /dev/null +++ b/config_gen/generators/codeWeb.ts @@ -0,0 +1,5 @@ +import { ConfigEntry, ConfigEntryType } from "../types" + +export default function (data: ConfigEntry[]): string { + return "" +} diff --git a/config_gen/generators/doc.ts b/config_gen/generators/doc.ts new file mode 100644 index 00000000..cbc3334e --- /dev/null +++ b/config_gen/generators/doc.ts @@ -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; +} + diff --git a/config_gen/generators/index.ts b/config_gen/generators/index.ts new file mode 100644 index 00000000..11136b75 --- /dev/null +++ b/config_gen/generators/index.ts @@ -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) { +} +export const cli = function (data: Array) { +} +export const web = function (data: Array) { +} +export const schema = function (data: Array) { +} \ No newline at end of file diff --git a/config_gen/index.ts b/config_gen/index.ts new file mode 100644 index 00000000..3b25004f --- /dev/null +++ b/config_gen/index.ts @@ -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: is required") + } + break; + default: + console.log(`Usage: + c, code [core, cli, web, schema] + d, doc `); + break; +} diff --git a/config_gen/package-lock.json b/config_gen/package-lock.json new file mode 100644 index 00000000..8a69a9eb --- /dev/null +++ b/config_gen/package-lock.json @@ -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 + } + } +} diff --git a/config_gen/package.json b/config_gen/package.json new file mode 100644 index 00000000..9ed026f3 --- /dev/null +++ b/config_gen/package.json @@ -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": {} +} diff --git a/config_gen/types.ts b/config_gen/types.ts new file mode 100644 index 00000000..018bd5f5 --- /dev/null +++ b/config_gen/types.ts @@ -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, +} \ No newline at end of file diff --git a/config_gen/utils.ts b/config_gen/utils.ts new file mode 100644 index 00000000..85081b18 --- /dev/null +++ b/config_gen/utils.ts @@ -0,0 +1,5 @@ +export function trimEnd(text: string, trimChar: string): string { + return text.slice(-1) === trimChar + ? text.slice(0, -1) + : text; +} \ No newline at end of file