Refactor config code generation

This commit is contained in:
genteure 2022-06-10 16:34:25 +08:00
parent 440b102d70
commit d884eaa6a9
13 changed files with 263 additions and 311 deletions

View File

@ -107,12 +107,12 @@ namespace BililiveRecorder.Core.Config.V3
public string? FileNameRecordTemplate => this.GetPropertyValue<string>(); public string? FileNameRecordTemplate => this.GetPropertyValue<string>();
/// <summary> /// <summary>
/// 录制文件写入结束 Webhook 地址 每行一个 /// WebhookV1
/// </summary> /// </summary>
public string? WebHookUrls => this.GetPropertyValue<string>(); public string? WebHookUrls => this.GetPropertyValue<string>();
/// <summary> /// <summary>
/// Webhook v2 地址 每行一个 /// WebhookV2
/// </summary> /// </summary>
public string? WebHookUrlsV2 => this.GetPropertyValue<string>(); public string? WebHookUrlsV2 => this.GetPropertyValue<string>();
@ -122,17 +122,17 @@ namespace BililiveRecorder.Core.Config.V3
public bool WpfShowTitleAndArea => this.GetPropertyValue<bool>(); public bool WpfShowTitleAndArea => this.GetPropertyValue<bool>();
/// <summary> /// <summary>
/// 请求 API 时使用的 Cookie /// Cookie
/// </summary> /// </summary>
public string? Cookie => this.GetPropertyValue<string>(); public string? Cookie => this.GetPropertyValue<string>();
/// <summary> /// <summary>
/// 替换 api.live.bilibili.com 服务器为其他反代,可以支持在云服务器上录制 /// API Host
/// </summary> /// </summary>
public string? LiveApiHost => this.GetPropertyValue<string>(); public string? LiveApiHost => this.GetPropertyValue<string>();
/// <summary> /// <summary>
/// HTTP API 检查时间间隔 秒 /// 主动检查时间间隔 秒
/// </summary> /// </summary>
public uint TimingCheckInterval => this.GetPropertyValue<uint>(); public uint TimingCheckInterval => this.GetPropertyValue<uint>();
@ -157,12 +157,12 @@ namespace BililiveRecorder.Core.Config.V3
public uint TimingDanmakuRetry => this.GetPropertyValue<uint>(); public uint TimingDanmakuRetry => this.GetPropertyValue<uint>();
/// <summary> /// <summary>
/// 最大允许未收到直播数据时间 毫秒 /// 最大未收到直播数据时间 毫秒
/// </summary> /// </summary>
public uint TimingWatchdogTimeout => this.GetPropertyValue<uint>(); public uint TimingWatchdogTimeout => this.GetPropertyValue<uint>();
/// <summary> /// <summary>
/// 触发 <see cref="System.Xml.XmlWriter.Flush"/> 的弹幕个数 /// 触发刷新弹幕写入缓冲的个数
/// </summary> /// </summary>
public uint RecordDanmakuFlushInterval => this.GetPropertyValue<uint>(); public uint RecordDanmakuFlushInterval => this.GetPropertyValue<uint>();
@ -267,7 +267,7 @@ namespace BililiveRecorder.Core.Config.V3
public Optional<string?> OptionalFileNameRecordTemplate { get => this.GetPropertyValueOptional<string>(nameof(this.FileNameRecordTemplate)); set => this.SetPropertyValueOptional(value, nameof(this.FileNameRecordTemplate)); } public Optional<string?> OptionalFileNameRecordTemplate { get => this.GetPropertyValueOptional<string>(nameof(this.FileNameRecordTemplate)); set => this.SetPropertyValueOptional(value, nameof(this.FileNameRecordTemplate)); }
/// <summary> /// <summary>
/// 录制文件写入结束 Webhook 地址 每行一个 /// WebhookV1
/// </summary> /// </summary>
public string? WebHookUrls { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); } 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)); } public bool HasWebHookUrls { get => this.GetPropertyHasValue(nameof(this.WebHookUrls)); set => this.SetPropertyHasValue<string>(value, nameof(this.WebHookUrls)); }
@ -275,7 +275,7 @@ namespace BililiveRecorder.Core.Config.V3
public Optional<string?> OptionalWebHookUrls { get => this.GetPropertyValueOptional<string>(nameof(this.WebHookUrls)); set => this.SetPropertyValueOptional(value, nameof(this.WebHookUrls)); } public Optional<string?> OptionalWebHookUrls { get => this.GetPropertyValueOptional<string>(nameof(this.WebHookUrls)); set => this.SetPropertyValueOptional(value, nameof(this.WebHookUrls)); }
/// <summary> /// <summary>
/// Webhook v2 地址 每行一个 /// WebhookV2
/// </summary> /// </summary>
public string? WebHookUrlsV2 { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); } 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)); } public bool HasWebHookUrlsV2 { get => this.GetPropertyHasValue(nameof(this.WebHookUrlsV2)); set => this.SetPropertyHasValue<string>(value, nameof(this.WebHookUrlsV2)); }
@ -291,7 +291,7 @@ namespace BililiveRecorder.Core.Config.V3
public Optional<bool> OptionalWpfShowTitleAndArea { get => this.GetPropertyValueOptional<bool>(nameof(this.WpfShowTitleAndArea)); set => this.SetPropertyValueOptional(value, nameof(this.WpfShowTitleAndArea)); } public Optional<bool> OptionalWpfShowTitleAndArea { get => this.GetPropertyValueOptional<bool>(nameof(this.WpfShowTitleAndArea)); set => this.SetPropertyValueOptional(value, nameof(this.WpfShowTitleAndArea)); }
/// <summary> /// <summary>
/// 请求 API 时使用的 Cookie /// Cookie
/// </summary> /// </summary>
public string? Cookie { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); } 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)); } public bool HasCookie { get => this.GetPropertyHasValue(nameof(this.Cookie)); set => this.SetPropertyHasValue<string>(value, nameof(this.Cookie)); }
@ -299,7 +299,7 @@ namespace BililiveRecorder.Core.Config.V3
public Optional<string?> OptionalCookie { get => this.GetPropertyValueOptional<string>(nameof(this.Cookie)); set => this.SetPropertyValueOptional(value, nameof(this.Cookie)); } public Optional<string?> OptionalCookie { get => this.GetPropertyValueOptional<string>(nameof(this.Cookie)); set => this.SetPropertyValueOptional(value, nameof(this.Cookie)); }
/// <summary> /// <summary>
/// 替换 api.live.bilibili.com 服务器为其他反代,可以支持在云服务器上录制 /// API Host
/// </summary> /// </summary>
public string? LiveApiHost { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); } 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)); } public bool HasLiveApiHost { get => this.GetPropertyHasValue(nameof(this.LiveApiHost)); set => this.SetPropertyHasValue<string>(value, nameof(this.LiveApiHost)); }
@ -307,7 +307,7 @@ namespace BililiveRecorder.Core.Config.V3
public Optional<string?> OptionalLiveApiHost { get => this.GetPropertyValueOptional<string>(nameof(this.LiveApiHost)); set => this.SetPropertyValueOptional(value, nameof(this.LiveApiHost)); } public Optional<string?> OptionalLiveApiHost { get => this.GetPropertyValueOptional<string>(nameof(this.LiveApiHost)); set => this.SetPropertyValueOptional(value, nameof(this.LiveApiHost)); }
/// <summary> /// <summary>
/// HTTP API 检查时间间隔 秒 /// 主动检查时间间隔 秒
/// </summary> /// </summary>
public uint TimingCheckInterval { get => this.GetPropertyValue<uint>(); set => this.SetPropertyValue(value); } 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)); } public bool HasTimingCheckInterval { get => this.GetPropertyHasValue(nameof(this.TimingCheckInterval)); set => this.SetPropertyHasValue<uint>(value, nameof(this.TimingCheckInterval)); }
@ -347,7 +347,7 @@ namespace BililiveRecorder.Core.Config.V3
public Optional<uint> OptionalTimingDanmakuRetry { get => this.GetPropertyValueOptional<uint>(nameof(this.TimingDanmakuRetry)); set => this.SetPropertyValueOptional(value, nameof(this.TimingDanmakuRetry)); } public Optional<uint> OptionalTimingDanmakuRetry { get => this.GetPropertyValueOptional<uint>(nameof(this.TimingDanmakuRetry)); set => this.SetPropertyValueOptional(value, nameof(this.TimingDanmakuRetry)); }
/// <summary> /// <summary>
/// 最大允许未收到直播数据时间 毫秒 /// 最大未收到直播数据时间 毫秒
/// </summary> /// </summary>
public uint TimingWatchdogTimeout { get => this.GetPropertyValue<uint>(); set => this.SetPropertyValue(value); } 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)); } public bool HasTimingWatchdogTimeout { get => this.GetPropertyHasValue(nameof(this.TimingWatchdogTimeout)); set => this.SetPropertyHasValue<uint>(value, nameof(this.TimingWatchdogTimeout)); }
@ -355,7 +355,7 @@ namespace BililiveRecorder.Core.Config.V3
public Optional<uint> OptionalTimingWatchdogTimeout { get => this.GetPropertyValueOptional<uint>(nameof(this.TimingWatchdogTimeout)); set => this.SetPropertyValueOptional(value, nameof(this.TimingWatchdogTimeout)); } public Optional<uint> OptionalTimingWatchdogTimeout { get => this.GetPropertyValueOptional<uint>(nameof(this.TimingWatchdogTimeout)); set => this.SetPropertyValueOptional(value, nameof(this.TimingWatchdogTimeout)); }
/// <summary> /// <summary>
/// 触发 <see cref="System.Xml.XmlWriter.Flush"/> 的弹幕个数 /// 触发刷新弹幕写入缓冲的个数
/// </summary> /// </summary>
public uint RecordDanmakuFlushInterval { get => this.GetPropertyValue<uint>(); set => this.SetPropertyValue(value); } public uint RecordDanmakuFlushInterval { get => this.GetPropertyValue<uint>(); set => this.SetPropertyValue(value); }
public bool HasRecordDanmakuFlushInterval { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuFlushInterval)); set => this.SetPropertyHasValue<uint>(value, nameof(this.RecordDanmakuFlushInterval)); } public bool HasRecordDanmakuFlushInterval { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuFlushInterval)); set => this.SetPropertyHasValue<uint>(value, nameof(this.RecordDanmakuFlushInterval)); }
@ -409,31 +409,31 @@ namespace BililiveRecorder.Core.Config.V3
public bool RecordDanmakuGuard => true; public bool RecordDanmakuGuard => true;
public string RecordingQuality => "10000"; public string RecordingQuality => @"10000";
public string FileNameRecordTemplate => "{{ roomId }}-{{ name }}/录制-{{ roomId }}-{{ \"now\" | time_zone: \"Asia/Shanghai\" | format_date: \"yyyyMMdd-HHmmss-fff\" }}-{{ title }}.flv"; public string FileNameRecordTemplate => @"{{ roomId }}-{{ name }}/录制-{{ roomId }}-{{ ""now"" | time_zone: ""Asia/Shanghai"" | format_date: ""yyyyMMdd-HHmmss-fff"" }}-{{ title }}.flv";
public string WebHookUrls => string.Empty; public string WebHookUrls => @"";
public string WebHookUrlsV2 => string.Empty; public string WebHookUrlsV2 => @"";
public bool WpfShowTitleAndArea => true; public bool WpfShowTitleAndArea => true;
public string Cookie => string.Empty; public string Cookie => @"";
public string LiveApiHost => "https://api.live.bilibili.com"; public string LiveApiHost => @"https://api.live.bilibili.com";
public uint TimingCheckInterval => 10 * 60; public uint TimingCheckInterval => 600;
public uint TimingStreamRetry => 6 * 1000; public uint TimingStreamRetry => 6000;
public uint TimingStreamRetryNoQn => 90; public uint TimingStreamRetryNoQn => 90;
public uint TimingStreamConnect => 5 * 1000; public uint TimingStreamConnect => 5000;
public uint TimingDanmakuRetry => 9 * 1000; public uint TimingDanmakuRetry => 9000;
public uint TimingWatchdogTimeout => 10 * 1000; public uint TimingWatchdogTimeout => 10000;
public uint RecordDanmakuFlushInterval => 20; public uint RecordDanmakuFlushInterval => 20;
@ -441,7 +441,7 @@ namespace BililiveRecorder.Core.Config.V3
public AllowedAddressFamily NetworkTransportAllowedAddressFamily => AllowedAddressFamily.Any; public AllowedAddressFamily NetworkTransportAllowedAddressFamily => AllowedAddressFamily.Any;
public string UserScript => string.Empty; public string UserScript => @"";
} }

View File

@ -7,8 +7,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"FileNameRecordTemplate": { "FileNameRecordTemplate": {
"description": "录制文件名模板\n默认: \"{{ roomId }}-{{ name }}/录制-{{ roomId }}-{{ \"now\" | time_zone: \"Asia/Shanghai\" | format_date: \"yyyyMMdd-HHmmss-fff\" }}-{{ title }}.flv\"", "description": "录制文件名模板\n默认: {{ roomId }}-{{ name }}/录制-{{ roomId }}-{{ \"now\" | time_zone: \"Asia/Shanghai\" | format_date: \"yyyyMMdd-HHmmss-fff\" }}-{{ title }}.flv",
"markdownDescription": "录制文件名模板 \n默认: `\"{{ roomId }}-{{ name }}/录制-{{ roomId }}-{{ \"now\" | time_zone: \"Asia/Shanghai\" | format_date: \"yyyyMMdd-HHmmss-fff\" }}-{{ title }}.flv\" `\n\nTODO: config v3 新的文件名模板系统的文档还没有写", "markdownDescription": "录制文件名模板 \n默认: `{{ roomId }}-{{ name }}/录制-{{ roomId }}-{{ \"now\" | time_zone: \"Asia/Shanghai\" | format_date: \"yyyyMMdd-HHmmss-fff\" }}-{{ title }}.flv `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -24,7 +24,7 @@
}, },
"WebHookUrls": { "WebHookUrls": {
"description": "WebhookV1\n默认: ", "description": "WebhookV1\n默认: ",
"markdownDescription": "WebhookV1 \n默认: ` `\n\n具体文档见 [Webhook](/docs/basic/webhook/)", "markdownDescription": "WebhookV1 \n默认: ` `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -40,7 +40,7 @@
}, },
"WebHookUrlsV2": { "WebHookUrlsV2": {
"description": "WebhookV2\n默认: ", "description": "WebhookV2\n默认: ",
"markdownDescription": "WebhookV2 \n默认: ` `\n\n具体文档见 [Webhook](/docs/basic/webhook/)", "markdownDescription": "WebhookV2 \n默认: ` `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -56,7 +56,7 @@
}, },
"WpfShowTitleAndArea": { "WpfShowTitleAndArea": {
"description": "在界面显示标题和分区\n默认: true", "description": "在界面显示标题和分区\n默认: true",
"markdownDescription": "在界面显示标题和分区 \n默认: `true `\n\n只在桌面版WPF版有效", "markdownDescription": "在界面显示标题和分区 \n默认: `true `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -71,8 +71,8 @@
} }
}, },
"Cookie": { "Cookie": {
"description": "请求 API 时使用的 Cookie\n默认: (空字符串)", "description": "Cookie\n默认: ",
"markdownDescription": "请求 API 时使用的 Cookie \n默认: `(空字符串) `\n\n", "markdownDescription": "Cookie \n默认: ` `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -88,8 +88,8 @@
} }
}, },
"LiveApiHost": { "LiveApiHost": {
"description": "请求的 API Host\n默认: https://api.live.bilibili.com", "description": "API Host\n默认: https://api.live.bilibili.com",
"markdownDescription": "请求的 API Host \n默认: `https://api.live.bilibili.com `\n\n", "markdownDescription": "API Host \n默认: `https://api.live.bilibili.com `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -104,8 +104,8 @@
} }
}, },
"TimingCheckInterval": { "TimingCheckInterval": {
"description": "HTTP API 检查时间间隔 秒\n默认: 600 (10分)", "description": "主动检查时间间隔 秒\n默认: 600",
"markdownDescription": "HTTP API 检查时间间隔 秒 \n默认: `600 (10分) `\n\n", "markdownDescription": "主动检查时间间隔 秒 \n默认: `600 `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -122,8 +122,8 @@
} }
}, },
"TimingStreamRetry": { "TimingStreamRetry": {
"description": "录制断开重连时间间隔 毫秒\n默认: 6000 (6秒)", "description": "录制断开重连时间间隔 毫秒\n默认: 6000",
"markdownDescription": "录制断开重连时间间隔 毫秒 \n默认: `6000 (6秒) `\n\n", "markdownDescription": "录制断开重连时间间隔 毫秒 \n默认: `6000 `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -140,8 +140,8 @@
} }
}, },
"TimingStreamRetryNoQn": { "TimingStreamRetryNoQn": {
"description": "录制无指定画质重连时间间隔 秒\n默认: 90 (1.5分钟)", "description": "录制无指定画质重连时间间隔 秒\n默认: 90",
"markdownDescription": "录制无指定画质重连时间间隔 秒 \n默认: `90 (1.5分钟) `\n\n", "markdownDescription": "录制无指定画质重连时间间隔 秒 \n默认: `90 `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -158,8 +158,8 @@
} }
}, },
"TimingStreamConnect": { "TimingStreamConnect": {
"description": "连接直播服务器超时时间 毫秒\n默认: 5000 (5秒)", "description": "连接直播服务器超时时间 毫秒\n默认: 5000",
"markdownDescription": "连接直播服务器超时时间 毫秒 \n默认: `5000 (5秒) `\n\n", "markdownDescription": "连接直播服务器超时时间 毫秒 \n默认: `5000 `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -176,8 +176,8 @@
} }
}, },
"TimingDanmakuRetry": { "TimingDanmakuRetry": {
"description": "弹幕服务器重连时间间隔 毫秒\n默认: 9000 (9秒)", "description": "弹幕服务器重连时间间隔 毫秒\n默认: 9000",
"markdownDescription": "弹幕服务器重连时间间隔 毫秒 \n默认: `9000 (9秒) `\n\n", "markdownDescription": "弹幕服务器重连时间间隔 毫秒 \n默认: `9000 `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -194,8 +194,8 @@
} }
}, },
"TimingWatchdogTimeout": { "TimingWatchdogTimeout": {
"description": "最大允许未收到直播数据时间 毫秒\n默认: 10000 (10秒)", "description": "最大未收到直播数据时间 毫秒\n默认: 10000",
"markdownDescription": "最大允许未收到直播数据时间 毫秒 \n默认: `10000 (10秒) `\n\n", "markdownDescription": "最大未收到直播数据时间 毫秒 \n默认: `10000 `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -256,8 +256,15 @@
"default": true "default": true
}, },
"Value": { "Value": {
"type": "AllowedAddressFamily", "type": "integer",
"default": "AllowedAddressFamily.Any" "default": 0,
"enum": [
-1,
0,
1,
2
],
"description": "-1: 由系统决定\n0: 任意 IPv4 或 IPv6\n1: 仅 IPv4\n2: IPv6"
} }
} }
}, },
@ -279,7 +286,7 @@
}, },
"RecordMode": { "RecordMode": {
"description": "录制模式\n默认: RecordMode.Standard", "description": "录制模式\n默认: RecordMode.Standard",
"markdownDescription": "录制模式 \n默认: `RecordMode.Standard `\n\n本设置项是一个 enum键值对应如下\n\n| 键 | 值 |\n|:--:|:--:|\n| RecordMode.Standard | 0 |\n| RecordMode.RawData | 1 |\n\n关于录制模式的说明见 [录制模式](/docs/basic/record_mode/)", "markdownDescription": "录制模式 \n默认: `RecordMode.Standard `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -300,7 +307,7 @@
}, },
"CuttingMode": { "CuttingMode": {
"description": "自动分段模式\n默认: CuttingMode.Disabled", "description": "自动分段模式\n默认: CuttingMode.Disabled",
"markdownDescription": "自动分段模式 \n默认: `CuttingMode.Disabled `\n\n本设置项是一个 enum键值对应如下\n\n| 键 | 值 |\n|:--:|:--:|\n| CuttingMode.Disabled | 0 |\n| CuttingMode.ByTime | 1 |\n| CuttingMode.BySize | 2 |", "markdownDescription": "自动分段模式 \n默认: `CuttingMode.Disabled `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -322,7 +329,7 @@
}, },
"CuttingNumber": { "CuttingNumber": {
"description": "自动分段数值\n默认: 100", "description": "自动分段数值\n默认: 100",
"markdownDescription": "自动分段数值 \n默认: `100 `\n\n根据 CuttingMode 设置的不同: \n当按时长分段时本设置的单位为分钟。 \n当按大小分段时本设置的单位为MiB。", "markdownDescription": "自动分段数值 \n默认: `100 `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -340,7 +347,7 @@
}, },
"RecordDanmaku": { "RecordDanmaku": {
"description": "弹幕录制\n默认: false", "description": "弹幕录制\n默认: false",
"markdownDescription": "弹幕录制 \n默认: `false `\n\n是否录制弹幕,`true` 为录制,`false` 为不录制。\n\n本设置同时是所有“弹幕录制”的总开关当本设置为 `false` 时其他所有“弹幕录制”设置无效不会写入弹幕XML文件。", "markdownDescription": "弹幕录制 \n默认: `false `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -356,7 +363,7 @@
}, },
"RecordDanmakuRaw": { "RecordDanmakuRaw": {
"description": "弹幕录制-原始数据\n默认: false", "description": "弹幕录制-原始数据\n默认: false",
"markdownDescription": "弹幕录制-原始数据 \n默认: `false `\n\n是否记录原始 JSON 数据。\n\n弹幕原始数据会保存到 XML 文件每一条弹幕数据的 `raw` attribute 上。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。", "markdownDescription": "弹幕录制-原始数据 \n默认: `false `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -372,7 +379,7 @@
}, },
"RecordDanmakuSuperChat": { "RecordDanmakuSuperChat": {
"description": "弹幕录制-SuperChat\n默认: true", "description": "弹幕录制-SuperChat\n默认: true",
"markdownDescription": "弹幕录制-SuperChat \n默认: `true `\n\n是否记录 SuperChat。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。", "markdownDescription": "弹幕录制-SuperChat \n默认: `true `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -388,7 +395,7 @@
}, },
"RecordDanmakuGift": { "RecordDanmakuGift": {
"description": "弹幕录制-礼物\n默认: false", "description": "弹幕录制-礼物\n默认: false",
"markdownDescription": "弹幕录制-礼物 \n默认: `false `\n\n是否记录礼物。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。", "markdownDescription": "弹幕录制-礼物 \n默认: `false `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -404,7 +411,7 @@
}, },
"RecordDanmakuGuard": { "RecordDanmakuGuard": {
"description": "弹幕录制-上船\n默认: true", "description": "弹幕录制-上船\n默认: true",
"markdownDescription": "弹幕录制-上船 \n默认: `true `\n\n是否记录上船(购买舰长)。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。", "markdownDescription": "弹幕录制-上船 \n默认: `true `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -420,7 +427,7 @@
}, },
"RecordingQuality": { "RecordingQuality": {
"description": "直播画质\n默认: 10000", "description": "直播画质\n默认: 10000",
"markdownDescription": "直播画质 \n默认: `10000 `\n\n录制的直播画质 qn 值,以英文逗号分割,靠前的优先。\n\n**注意**(从录播姬 1.3.10 开始):\n\n- 所有主播刚开播时都是只有“原画”的,如果选择不录原画会导致直播开头漏录。\n- 如果设置的录制画质里没有原画,但是主播只有原画画质,会导致不能录制直播。\n- 录播姬不会为了切换录制的画质主动断开录制。\n\n画质 | qn 值\n:--:|:--:\n4K | 20000\n原画 | 10000\n蓝光(杜比) | 401\n蓝光 | 400\n超清 | 250\n高清 | 150\n流畅 | 80", "markdownDescription": "直播画质 \n默认: `10000 `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -441,8 +448,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"RoomId": { "RoomId": {
"description": "房间号\n默认: default", "description": "房间号\n默认: ",
"markdownDescription": "房间号 \n默认: `default `\n\n", "markdownDescription": "房间号 \n默认: ` `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -453,13 +460,14 @@
"Value": { "Value": {
"type": "integer", "type": "integer",
"minimum": -2147483648, "minimum": -2147483648,
"maximum": 2147483647 "maximum": 2147483647,
"default": ""
} }
} }
}, },
"AutoRecord": { "AutoRecord": {
"description": "自动录制\n默认: default", "description": "自动录制\n默认: ",
"markdownDescription": "自动录制 \n默认: `default `\n\n设为 `true` 为启用自动录制,`false` 为不自动录制。", "markdownDescription": "自动录制 \n默认: ` `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -468,13 +476,14 @@
"default": true "default": true
}, },
"Value": { "Value": {
"type": "boolean" "type": "boolean",
"default": ""
} }
} }
}, },
"RecordMode": { "RecordMode": {
"description": "录制模式\n默认: RecordMode.Standard", "description": "录制模式\n默认: RecordMode.Standard",
"markdownDescription": "录制模式 \n默认: `RecordMode.Standard `\n\n本设置项是一个 enum键值对应如下\n\n| 键 | 值 |\n|:--:|:--:|\n| RecordMode.Standard | 0 |\n| RecordMode.RawData | 1 |\n\n关于录制模式的说明见 [录制模式](/docs/basic/record_mode/)", "markdownDescription": "录制模式 \n默认: `RecordMode.Standard `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -495,7 +504,7 @@
}, },
"CuttingMode": { "CuttingMode": {
"description": "自动分段模式\n默认: CuttingMode.Disabled", "description": "自动分段模式\n默认: CuttingMode.Disabled",
"markdownDescription": "自动分段模式 \n默认: `CuttingMode.Disabled `\n\n本设置项是一个 enum键值对应如下\n\n| 键 | 值 |\n|:--:|:--:|\n| CuttingMode.Disabled | 0 |\n| CuttingMode.ByTime | 1 |\n| CuttingMode.BySize | 2 |", "markdownDescription": "自动分段模式 \n默认: `CuttingMode.Disabled `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -517,7 +526,7 @@
}, },
"CuttingNumber": { "CuttingNumber": {
"description": "自动分段数值\n默认: 100", "description": "自动分段数值\n默认: 100",
"markdownDescription": "自动分段数值 \n默认: `100 `\n\n根据 CuttingMode 设置的不同: \n当按时长分段时本设置的单位为分钟。 \n当按大小分段时本设置的单位为MiB。", "markdownDescription": "自动分段数值 \n默认: `100 `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -535,7 +544,7 @@
}, },
"RecordDanmaku": { "RecordDanmaku": {
"description": "弹幕录制\n默认: false", "description": "弹幕录制\n默认: false",
"markdownDescription": "弹幕录制 \n默认: `false `\n\n是否录制弹幕,`true` 为录制,`false` 为不录制。\n\n本设置同时是所有“弹幕录制”的总开关当本设置为 `false` 时其他所有“弹幕录制”设置无效不会写入弹幕XML文件。", "markdownDescription": "弹幕录制 \n默认: `false `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -551,7 +560,7 @@
}, },
"RecordDanmakuRaw": { "RecordDanmakuRaw": {
"description": "弹幕录制-原始数据\n默认: false", "description": "弹幕录制-原始数据\n默认: false",
"markdownDescription": "弹幕录制-原始数据 \n默认: `false `\n\n是否记录原始 JSON 数据。\n\n弹幕原始数据会保存到 XML 文件每一条弹幕数据的 `raw` attribute 上。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。", "markdownDescription": "弹幕录制-原始数据 \n默认: `false `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -567,7 +576,7 @@
}, },
"RecordDanmakuSuperChat": { "RecordDanmakuSuperChat": {
"description": "弹幕录制-SuperChat\n默认: true", "description": "弹幕录制-SuperChat\n默认: true",
"markdownDescription": "弹幕录制-SuperChat \n默认: `true `\n\n是否记录 SuperChat。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。", "markdownDescription": "弹幕录制-SuperChat \n默认: `true `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -583,7 +592,7 @@
}, },
"RecordDanmakuGift": { "RecordDanmakuGift": {
"description": "弹幕录制-礼物\n默认: false", "description": "弹幕录制-礼物\n默认: false",
"markdownDescription": "弹幕录制-礼物 \n默认: `false `\n\n是否记录礼物。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。", "markdownDescription": "弹幕录制-礼物 \n默认: `false `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -599,7 +608,7 @@
}, },
"RecordDanmakuGuard": { "RecordDanmakuGuard": {
"description": "弹幕录制-上船\n默认: true", "description": "弹幕录制-上船\n默认: true",
"markdownDescription": "弹幕录制-上船 \n默认: `true `\n\n是否记录上船(购买舰长)。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。", "markdownDescription": "弹幕录制-上船 \n默认: `true `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -615,7 +624,7 @@
}, },
"RecordingQuality": { "RecordingQuality": {
"description": "直播画质\n默认: 10000", "description": "直播画质\n默认: 10000",
"markdownDescription": "直播画质 \n默认: `10000 `\n\n录制的直播画质 qn 值,以英文逗号分割,靠前的优先。\n\n**注意**(从录播姬 1.3.10 开始):\n\n- 所有主播刚开播时都是只有“原画”的,如果选择不录原画会导致直播开头漏录。\n- 如果设置的录制画质里没有原画,但是主播只有原画画质,会导致不能录制直播。\n- 录播姬不会为了切换录制的画质主动断开录制。\n\n画质 | qn 值\n:--:|:--:\n4K | 20000\n原画 | 10000\n蓝光(杜比) | 401\n蓝光 | 400\n超清 | 250\n高清 | 150\n流畅 | 80", "markdownDescription": "直播画质 \n默认: `10000 `\n\n",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -641,10 +650,10 @@
"properties": { "properties": {
"$schema": { "$schema": {
"type": "string", "type": "string",
"default": "https://raw.githubusercontent.com/Bililive/BililiveRecorder/dev-1.3/configV2.schema.json" "default": "https://raw.githubusercontent.com/BililiveRecorder/BililiveRecorder/dev/configV3.schema.json"
}, },
"version": { "version": {
"const": 2 "const": 3
}, },
"global": { "global": {
"$ref": "#/definitions/global-config" "$ref": "#/definitions/global-config"

View File

@ -1,246 +1,206 @@
import { ConfigEntry, ConfigEntryType } from './types' import { ConfigEntry } from './types'
export const data: Array<ConfigEntry> = [ export const data: Array<ConfigEntry> = [
{ {
name: "RoomId", id: "RoomId",
description: "房间号", name: "房间号",
type: "int", type: "int",
configType: "roomOnly", configType: "roomOnly",
defaultValue: "default", default: "",
webReadonly: true, webReadonly: true
markdown: ""
}, },
{ {
name: "AutoRecord", id: "AutoRecord",
description: "自动录制", name: "自动录制",
type: "bool", type: "bool",
configType: "roomOnly", configType: "roomOnly",
defaultValue: "default", default: ""
markdown: "设为 `true` 为启用自动录制,`false` 为不自动录制。"
}, },
{ {
name: "RecordMode", id: "RecordMode",
description: "录制模式", name: "录制模式",
type: "RecordMode", type: "RecordMode",
configType: "room", configType: "room",
defaultValue: "RecordMode.Standard", default: "RecordMode.Standard"
markdown: "本设置项是一个 enum键值对应如下\n\n| 键 | 值 |\n|:--:|:--:|\n| RecordMode.Standard | 0 |\n| RecordMode.RawData | 1 |\n\n关于录制模式的说明见 [录制模式](/docs/basic/record_mode/)"
}, },
{ {
name: "CuttingMode", id: "CuttingMode",
description: "自动分段模式", name: "自动分段模式",
type: "CuttingMode", type: "CuttingMode",
configType: "room", configType: "room",
defaultValue: "CuttingMode.Disabled", default: "CuttingMode.Disabled"
markdown: "本设置项是一个 enum键值对应如下\n\n| 键 | 值 |\n|:--:|:--:|\n| CuttingMode.Disabled | 0 |\n| CuttingMode.ByTime | 1 |\n| CuttingMode.BySize | 2 |"
}, },
{ {
name: "CuttingNumber", id: "CuttingNumber",
description: "自动分段数值", name: "自动分段数值",
type: "uint", type: "uint",
configType: "room", configType: "room",
defaultValue: "100", default: 100
markdown: "根据 CuttingMode 设置的不同: \n当按时长分段时本设置的单位为分钟。 \n当按大小分段时本设置的单位为MiB。"
}, },
{ {
name: "RecordDanmaku", id: "RecordDanmaku",
description: "弹幕录制", name: "弹幕录制",
type: "bool", type: "bool",
configType: "room", configType: "room",
defaultValue: "false", default: false
markdown: "是否录制弹幕,`true` 为录制,`false` 为不录制。\n\n本设置同时是所有“弹幕录制”的总开关当本设置为 `false` 时其他所有“弹幕录制”设置无效不会写入弹幕XML文件。"
}, },
{ {
name: "RecordDanmakuRaw", id: "RecordDanmakuRaw",
description: "弹幕录制-原始数据", name: "弹幕录制-原始数据",
type: "bool", type: "bool",
configType: "room", configType: "room",
defaultValue: "false", default: false
markdown: "是否记录原始 JSON 数据。\n\n弹幕原始数据会保存到 XML 文件每一条弹幕数据的 `raw` attribute 上。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。"
}, },
{ {
name: "RecordDanmakuSuperChat", id: "RecordDanmakuSuperChat",
description: "弹幕录制-SuperChat", name: "弹幕录制-SuperChat",
type: "bool", type: "bool",
configType: "room", configType: "room",
defaultValue: "true", default: true
markdown: "是否记录 SuperChat。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。"
}, },
{ {
name: "RecordDanmakuGift", id: "RecordDanmakuGift",
description: "弹幕录制-礼物", name: "弹幕录制-礼物",
type: "bool", type: "bool",
configType: "room", configType: "room",
defaultValue: "false", default: false
markdown: "是否记录礼物。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。"
}, },
{ {
name: "RecordDanmakuGuard", id: "RecordDanmakuGuard",
description: "弹幕录制-上船", name: "弹幕录制-上船",
type: "bool", type: "bool",
configType: "room", configType: "room",
defaultValue: "true", default: true
markdown: "是否记录上船(购买舰长)。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。"
}, },
{ {
name: "RecordingQuality", id: "RecordingQuality",
name: "直播画质",
type: "string?", type: "string?",
configType: "room", configType: "room",
description: "直播画质", default: "10000",
defaultValue: "\"10000\"",
defaultValueDescription: "10000",
markdown: "录制的直播画质 qn 值,以英文逗号分割,靠前的优先。\n\n**注意**(从录播姬 1.3.10 开始):\n\n- 所有主播刚开播时都是只有“原画”的,如果选择不录原画会导致直播开头漏录。\n- 如果设置的录制画质里没有原画,但是主播只有原画画质,会导致不能录制直播。\n- 录播姬不会为了切换录制的画质主动断开录制。\n\n画质 | qn 值\n:--:|:--:\n4K | 20000\n原画 | 10000\n蓝光(杜比) | 401\n蓝光 | 400\n超清 | 250\n高清 | 150\n流畅 | 80"
}, },
{ {
name: "FileNameRecordTemplate", id: "FileNameRecordTemplate",
description: "录制文件名模板", name: "录制文件名模板",
type: "string?", type: "string?",
configType: "globalOnly", configType: "globalOnly",
defaultValue: "\"{{ roomId }}-{{ name }}/录制-{{ roomId }}-{{ \\\"now\\\" | time_zone: \\\"Asia/Shanghai\\\" | format_date: \\\"yyyyMMdd-HHmmss-fff\\\" }}-{{ title }}.flv\"", default: '{{ roomId }}-{{ name }}/录制-{{ roomId }}-{{ "now" | time_zone: "Asia/Shanghai" | format_date: "yyyyMMdd-HHmmss-fff" }}-{{ title }}.flv',
defaultValueDescription: "\"{{ roomId }}-{{ name }}/录制-{{ roomId }}-{{ \"now\" | time_zone: \"Asia/Shanghai\" | format_date: \"yyyyMMdd-HHmmss-fff\" }}-{{ title }}.flv\"",
markdown: "TODO: config v3 新的文件名模板系统的文档还没有写"
}, },
{ {
name: "WebHookUrls", id: "WebHookUrls",
description: "WebhookV1", name: "WebhookV1",
type: "string?", type: "string?",
configType: "globalOnly", configType: "globalOnly",
defaultValue: "string.Empty", default: "",
xmlComment: "录制文件写入结束 Webhook 地址 每行一个",
markdown: "具体文档见 [Webhook](/docs/basic/webhook/)"
}, },
{ {
name: "WebHookUrlsV2", id: "WebHookUrlsV2",
description: "WebhookV2", name: "WebhookV2",
type: "string?", type: "string?",
configType: "globalOnly", configType: "globalOnly",
defaultValue: "string.Empty", default: "",
xmlComment: "Webhook v2 地址 每行一个",
markdown: "具体文档见 [Webhook](/docs/basic/webhook/)"
}, },
{ {
name: "WpfShowTitleAndArea", id: "WpfShowTitleAndArea",
description: "在界面显示标题和分区", name: "在界面显示标题和分区",
type: "bool", type: "bool",
configType: "globalOnly", configType: "globalOnly",
defaultValue: "true", default: true
markdown: "只在桌面版WPF版有效"
}, },
{ {
id: "Cookie",
name: "Cookie", name: "Cookie",
description: "请求 API 时使用的 Cookie",
type: "string?", type: "string?",
configType: "globalOnly", configType: "globalOnly",
advancedConfig: true, advancedConfig: true,
defaultValue: "string.Empty", default: "",
defaultValueDescription: "(空字符串)",
markdown: ""
}, },
{ {
name: "LiveApiHost", id: "LiveApiHost",
description: "请求的 API Host", name: "API Host",
type: "string?", type: "string?",
configType: "globalOnly", configType: "globalOnly",
advancedConfig: true, advancedConfig: true,
defaultValue: "\"https://api.live.bilibili.com\"", default: "https://api.live.bilibili.com",
xmlComment: "替换 api.live.bilibili.com 服务器为其他反代,可以支持在云服务器上录制",
markdown: ""
}, },
{ {
name: "TimingCheckInterval", id: "TimingCheckInterval",
description: "HTTP API 检查时间间隔 秒", name: "主动检查时间间隔 秒",
type: "uint", type: "uint",
configType: "globalOnly", configType: "globalOnly",
advancedConfig: true, advancedConfig: true,
defaultValue: "10 * 60", default: 10 * 60,
defaultValueDescription: "600 (10分)",
markdown: ""
}, },
{ {
name: "TimingStreamRetry", id: "TimingStreamRetry",
description: "录制断开重连时间间隔 毫秒", name: "录制断开重连时间间隔 毫秒",
type: "uint", type: "uint",
configType: "globalOnly", configType: "globalOnly",
advancedConfig: true, advancedConfig: true,
defaultValue: "6 * 1000", default: 6 * 1000,
defaultValueDescription: "6000 (6秒)",
markdown: ""
}, },
{ {
name: "TimingStreamRetryNoQn", id: "TimingStreamRetryNoQn",
description: "录制无指定画质重连时间间隔 秒", name: "录制无指定画质重连时间间隔 秒",
type: "uint", type: "uint",
configType: "globalOnly", configType: "globalOnly",
advancedConfig: true, advancedConfig: true,
defaultValue: "90", default: 90,
defaultValueDescription: "90 (1.5分钟)",
markdown: ""
}, },
{ {
name: "TimingStreamConnect", id: "TimingStreamConnect",
description: "连接直播服务器超时时间 毫秒", name: "连接直播服务器超时时间 毫秒",
type: "uint", type: "uint",
configType: "globalOnly", configType: "globalOnly",
advancedConfig: true, advancedConfig: true,
defaultValue: "5 * 1000", default: 5 * 1000,
defaultValueDescription: "5000 (5秒)",
markdown: ""
}, },
{ {
name: "TimingDanmakuRetry", id: "TimingDanmakuRetry",
description: "弹幕服务器重连时间间隔 毫秒", name: "弹幕服务器重连时间间隔 毫秒",
type: "uint", type: "uint",
configType: "globalOnly", configType: "globalOnly",
advancedConfig: true, advancedConfig: true,
defaultValue: "9 * 1000", default: 9 * 1000,
defaultValueDescription: "9000 (9秒)",
markdown: ""
}, },
{ {
name: "TimingWatchdogTimeout", id: "TimingWatchdogTimeout",
description: "最大允许未收到直播数据时间 毫秒", name: "最大未收到直播数据时间 毫秒",
type: "uint", type: "uint",
configType: "globalOnly", configType: "globalOnly",
advancedConfig: true, advancedConfig: true,
defaultValue: "10 * 1000", default: 10 * 1000,
defaultValueDescription: "10000 (10秒)",
markdown: ""
}, },
{ {
name: "RecordDanmakuFlushInterval", id: "RecordDanmakuFlushInterval",
description: "触发刷新弹幕写入缓冲的个数", name: "触发刷新弹幕写入缓冲的个数",
type: "uint", type: "uint",
configType: "globalOnly", configType: "globalOnly",
advancedConfig: true, advancedConfig: true,
defaultValue: "20", default: 20,
xmlComment: "触发 <see cref=\"System.Xml.XmlWriter.Flush\"/> 的弹幕个数",
markdown: ""
}, },
{ {
name: "NetworkTransportUseSystemProxy", id: "NetworkTransportUseSystemProxy",
description: "是否使用系统代理", name: "是否使用系统代理",
type: "bool", type: "bool",
defaultValue: "false", default: false,
configType: "globalOnly", configType: "globalOnly",
advancedConfig: true, advancedConfig: true
markdown: ""
}, },
{ {
name: "NetworkTransportAllowedAddressFamily", id: "NetworkTransportAllowedAddressFamily",
description: "允许使用的 IP 网络类型", name: "允许使用的 IP 网络类型",
type: "AllowedAddressFamily", type: "AllowedAddressFamily",
defaultValue: "AllowedAddressFamily.Any", default: "AllowedAddressFamily.Any",
configType: "globalOnly", configType: "globalOnly",
advancedConfig: true, advancedConfig: true
markdown: ""
}, },
{ {
name: "UserScript", id: "UserScript",
description: "自定义脚本", name: "自定义脚本",
type: "string?", type: "string?",
defaultValue: "string.Empty", default: "",
configType: "globalOnly", configType: "globalOnly",
advancedConfig: true, advancedConfig: true
markdown: ""
}, },
]; ];

View File

@ -15,7 +15,7 @@ export default function (data: ConfigEntry[]): string {
{ {
[Description("[grey]Exit[/]")] [Description("[grey]Exit[/]")]
Exit, Exit,
${data.filter(x => x.configType != 'roomOnly').map(x => x.name).join(",\n")} ${data.filter(x => x.configType != 'roomOnly').map(x => x.id).join(",\n")}
}`; }`;
result += ` result += `
@ -23,7 +23,7 @@ export default function (data: ConfigEntry[]): string {
{ {
[Description("[grey]Exit[/]")] [Description("[grey]Exit[/]")]
Exit, Exit,
${data.filter(x => x.configType != 'globalOnly').map(x => x.name).join(",\n")} ${data.filter(x => x.configType != 'globalOnly').map(x => x.id).join(",\n")}
}`; }`;
@ -37,11 +37,11 @@ export default function (data: ConfigEntry[]): string {
{ {
${data ${data
.filter(x => x.configType != 'roomOnly') .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 });`) .map(r => `GlobalConfig.Add(GlobalConfigProperties.${r.id}, new ConfigInstruction<GlobalConfig, ${trimEnd(r.type, '?')}>(config => config.Has${r.id} = false, (config, value) => config.${r.id} = value) { Name = "${r.id}", CanBeOptional = true });`)
.join("\n") .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")} ${data.filter(x => x.configType != 'globalOnly').map(r => `RoomConfig.Add(RoomConfigProperties.${r.id}, new ConfigInstruction<RoomConfig, ${trimEnd(r.type, '?')}>(config => config.Has${r.id} = false, (config, value) => config.${r.id} = value) { Name = "${r.id}", CanBeOptional = ${r.configType != 'roomOnly'} });`).join("\n")}
} }
} }
` `

View File

@ -1,5 +1,6 @@
import { ConfigEntry, ConfigEntryType } from "../types" import { ConfigEntry, ConfigEntryType } from "../types"
import { trimEnd } from "../utils"; import { trimEnd } from "../utils";
import { getConfigDefaultValueText } from "../utils";
export default function (data: ConfigEntry[]): string { export default function (data: ConfigEntry[]): string {
let result = `using System.ComponentModel; let result = `using System.ComponentModel;
@ -12,16 +13,16 @@ namespace BililiveRecorder.Core.Config.V3
`; `;
function write_property(r: ConfigEntry) { function write_property(r: ConfigEntry) {
result += `/// <summary>\n/// ${r.xmlComment ?? r.description}\n/// </summary>\n`; result += `/// <summary>\n/// ${r.name}\n/// </summary>\n`;
result += `public ${r.type} ${r.name} { get => this.GetPropertyValue<${trimEnd(r.type, '?')}>(); set => this.SetPropertyValue(value); }\n`; result += `public ${r.type} ${r.id} { 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 += `public bool Has${r.id} { get => this.GetPropertyHasValue(nameof(this.${r.id})); set => this.SetPropertyHasValue<${trimEnd(r.type, '?')}>(value, nameof(this.${r.id})); }\n`;
result += `[JsonProperty(nameof(${r.name})), EditorBrowsable(EditorBrowsableState.Never)]\n`; result += `[JsonProperty(nameof(${r.id})), 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`; result += `public Optional<${r.type}> Optional${r.id} { get => this.GetPropertyValueOptional<${trimEnd(r.type, '?')}>(nameof(this.${r.id})); set => this.SetPropertyValueOptional(value, nameof(this.${r.id})); }\n\n`;
} }
function write_readonly_property(r: ConfigEntry) { function write_readonly_property(r: ConfigEntry) {
result += `/// <summary>\n/// ${r.xmlComment ?? r.description}\n/// </summary>\n`; result += `/// <summary>\n/// ${r.name}\n/// </summary>\n`;
result += `public ${r.type} ${r.name} => this.GetPropertyValue<${trimEnd(r.type, '?')}>();\n\n`; result += `public ${r.type} ${r.id} => this.GetPropertyValue<${trimEnd(r.type, '?')}>();\n\n`;
} }
{ {
@ -54,7 +55,7 @@ private DefaultConfig() {}\n\n`;
data data
.filter(x => x.configType != 'roomOnly') .filter(x => x.configType != 'roomOnly')
.forEach(r => { .forEach(r => {
result += `public ${trimEnd(r.type, '?')} ${r.name} => ${r.defaultValue};\n\n`; result += `public ${trimEnd(r.type, '?')} ${r.id} => ${getConfigDefaultValueText(r)};\n\n`;
}); });
result += "}\n\n"; result += "}\n\n";

View File

@ -1,49 +1,42 @@
import { ConfigEntry, ConfigEntryType } from "../types" import { ConfigEntry, ConfigEntryType } from "../types"
function tryEvalValue(str: string) { function mapTypeToJsonSchema(id: string, type: string, defaultValue: any) {
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) { switch (type) {
case "RecordMode": case "RecordMode":
return { type: "integer", default: 0, enum: [0, 1], "description": "0: Standard\n1: Raw" }; return { type: "integer", default: 0, enum: [0, 1], "description": "0: Standard\n1: Raw" };
case "CuttingMode": case "CuttingMode":
return { type: "integer", default: 0, enum: [0, 1, 2], "description": "0: 禁用\n1: 根据时间切割\n2: 根据文件大小切割" }; return { type: "integer", default: 0, enum: [0, 1, 2], "description": "0: 禁用\n1: 根据时间切割\n2: 根据文件大小切割" };
case "AllowedAddressFamily":
return { type: "integer", default: 0, enum: [-1, 0, 1, 2], "description": "-1: 由系统决定\n0: 任意 IPv4 或 IPv6\n1: 仅 IPv4\n2: IPv6" };
case "uint": case "uint":
return { type: "integer", minimum: 0, maximum: 4294967295, default: tryEvalValue(defaultValue) }; return { type: "integer", minimum: 0, maximum: 4294967295, default: defaultValue };
case "int": case "int":
return { type: "integer", minimum: -2147483648, maximum: 2147483647, default: tryEvalValue(defaultValue) }; return { type: "integer", minimum: -2147483648, maximum: 2147483647, default: defaultValue };
case "bool": case "bool":
return { type: "boolean", default: tryEvalValue(defaultValue) }; return { type: "boolean", default: defaultValue };
case "string": case "string":
case "string?": case "string?":
if (name === 'Cookie') { if (id === 'Cookie') {
return { type: "string", pattern: "^(\S+=\S+;? ?)*$", maxLength: 4096, }; return { type: "string", pattern: "^(\S+=\S+;? ?)*$", maxLength: 4096, };
} }
return { type: "string", default: mapCSharpString(defaultValue) }; return { type: "string", default: defaultValue };
default: default:
return { type, default: defaultValue }; return { type, default: defaultValue };
} }
} }
function buildProperty(target: { [i: string]: any }, config: ConfigEntry) { function buildProperty(target: { [i: string]: any }, config: ConfigEntry) {
const typeObj = mapTypeToJsonSchema(config.name, config.type, config.defaultValue); const typeObj = mapTypeToJsonSchema(config.id, config.type, config.default);
if (config.defaultValue === 'default') if (config.default === 'default')
delete typeObj['default']; delete typeObj['default'];
target[config.name] = { target[config.id] = {
description: config.description description: config.name
+ '\n默认: ' + (!config.defaultValueDescription ? mapCSharpString(config.defaultValue) : config.defaultValueDescription), + '\n默认: ' + config.default,
markdownDescription: config.description markdownDescription: config.name
+ ' \n默认: `' + (!config.defaultValueDescription ? mapCSharpString(config.defaultValue) : config.defaultValueDescription) + ' \n默认: `' + config.default
+ ' `\n\n' + config.markdown, + ' `\n\n',
type: "object", type: "object",
additionalProperties: false, additionalProperties: false,
properties: { properties: {
@ -89,10 +82,10 @@ export default function (data: ConfigEntry[]): string {
"properties": { "properties": {
"$schema": { "$schema": {
"type": "string", "type": "string",
"default": "https://raw.githubusercontent.com/Bililive/BililiveRecorder/dev-1.3/configV2.schema.json" "default": "https://raw.githubusercontent.com/BililiveRecorder/BililiveRecorder/dev/configV3.schema.json"
}, },
"version": { "version": {
"const": 2 "const": 3
}, },
"global": { "global": {
"$ref": "#/definitions/global-config" "$ref": "#/definitions/global-config"

View File

@ -10,41 +10,41 @@ using HierarchicalPropertyDefault;
`; `;
function write_query_graphType_property(r: ConfigEntry) { function write_query_graphType_property(r: ConfigEntry) {
if (r.configType == "roomOnly") { if (r.configType == "roomOnly") {
result += `this.Field(x => x.${r.name});\n`; result += `this.Field(x => x.${r.id});\n`;
} else { } else {
result += `this.Field(x => x.Optional${r.name}, type: typeof(HierarchicalOptionalType<${trimEnd(r.type, '?')}>));\n`; result += `this.Field(x => x.Optional${r.id}, type: typeof(HierarchicalOptionalType<${trimEnd(r.type, '?')}>));\n`;
} }
} }
function write_rest_dto_property(r: ConfigEntry) { function write_rest_dto_property(r: ConfigEntry) {
if (r.configType == "roomOnly") { if (r.configType == "roomOnly") {
result += `public ${r.type} ${r.name} { get; set; }\n`; result += `public ${r.type} ${r.id} { get; set; }\n`;
} else { } else {
result += `public Optional<${r.type}> Optional${r.name} { get; set; }\n`; result += `public Optional<${r.type}> Optional${r.id} { get; set; }\n`;
} }
} }
function write_mutation_graphType_property(r: ConfigEntry) { function write_mutation_graphType_property(r: ConfigEntry) {
if (r.configType == "roomOnly") { if (r.configType == "roomOnly") {
result += `this.Field(x => x.${r.name}, nullable: true);\n`; result += `this.Field(x => x.${r.id}, nullable: true);\n`;
} else { } else {
result += `this.Field(x => x.Optional${r.name}, nullable: true, type: typeof(HierarchicalOptionalInputType<${trimEnd(r.type, '?')}>));\n`; result += `this.Field(x => x.Optional${r.id}, nullable: true, type: typeof(HierarchicalOptionalInputType<${trimEnd(r.type, '?')}>));\n`;
} }
} }
function write_mutation_dataType_property(r: ConfigEntry) { function write_mutation_dataType_property(r: ConfigEntry) {
if (r.configType == "roomOnly") { if (r.configType == "roomOnly") {
result += `public ${r.type}? ${r.name} { get; set; }\n`; result += `public ${r.type}? ${r.id} { get; set; }\n`;
} else { } else {
result += `public Optional<${r.type}>? Optional${r.name} { get; set; }\n`; result += `public Optional<${r.type}>? Optional${r.id} { get; set; }\n`;
} }
} }
function write_mutation_apply_method(r: ConfigEntry) { function write_mutation_apply_method(r: ConfigEntry) {
if (r.configType == "roomOnly") { if (r.configType == "roomOnly") {
result += `if (this.${r.name}.HasValue) config.${r.name} = this.${r.name}.Value;\n`; result += `if (this.${r.id}.HasValue) config.${r.id} = this.${r.id}.Value;\n`;
} else { } else {
result += `if (this.Optional${r.name}.HasValue) config.Optional${r.name} = this.Optional${r.name}.Value;\n`; result += `if (this.Optional${r.id}.HasValue) config.Optional${r.id} = this.Optional${r.id}.Value;\n`;
} }
} }
@ -129,7 +129,7 @@ using HierarchicalPropertyDefault;
data.filter(r => r.configType != "roomOnly") data.filter(r => r.configType != "roomOnly")
.forEach(r => { .forEach(r => {
result += `this.Field(x => x.${r.name});\n`; result += `this.Field(x => x.${r.id});\n`;
}); });
result += "}\n}\n\n"; result += "}\n}\n\n";

View File

@ -1,61 +1,26 @@
import { ConfigEntry } from "../types" import { ConfigEntry } from "../types"
import fs from "fs" import { statSync, writeFileSync } from "fs";
import { resolve } from "path" import { resolve } from "path"
import { data } from "../data"; import { data } from "../data";
import { trimEnd } from "../utils"; import { trimEnd } from "../utils";
export default function doc(path: string): void { export default function doc(path: string): void {
if (!fs.statSync(resolve(path, '_config.yml'))) { if (!statSync(resolve(path, 'mkdocs.yml'))) {
console.error('Check your path'); console.error('Check your path');
return; return;
} }
if (!fs.statSync(resolve(path, 'index.html'))) { if (!statSync(resolve(path, 'docs/user/settings.md'))) {
console.error('Check your path'); console.error('Check your path');
return; return;
} }
const targetPath = resolve(path, '_includes/generated_settings_list.md') const targetPath = resolve(path, 'data/brec_settings.json')
const text = buildMarkdown(data) const text = buildJson(data)
fs.writeFileSync(targetPath, text, { encoding: 'utf8' }); writeFileSync(targetPath, text, { encoding: 'utf8' });
} }
function buildMarkdown(data: ConfigEntry[]): string { function buildJson(data: ConfigEntry[]): string {
let result = ''; return JSON.stringify(data, null, 2);
// 目录
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'
// 高级设置说明
result += "## 高级设置\n\n"
result += "<span style=\"color:red\">重要说明</span>:一般用户通常不需要也不应该修改高级设置。 \n对各个 Timing 的修改可能会导致被B站服务器屏蔽、不能及时开始录制等问题。\n\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')
return result;
} }

View File

@ -5,7 +5,7 @@
"packages": { "packages": {
"": { "": {
"devDependencies": { "devDependencies": {
"@types/node": "^16.11.26", "@types/node": "^16.11.39",
"ts-node": "^10.2.0", "ts-node": "^10.2.0",
"tslib": "^2.3.1", "tslib": "^2.3.1",
"typescript": "^4.3.5" "typescript": "^4.3.5"
@ -57,9 +57,9 @@
"dev": true "dev": true
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "16.11.26", "version": "16.11.39",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-16.11.26.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.39.tgz",
"integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==", "integrity": "sha512-K0MsdV42vPwm9L6UwhIxMAOmcvH/1OoVkZyCgEtVu4Wx7sElGloy/W7kMBNe/oJ7V/jW9BVt1F6RahH6e7tPXw==",
"dev": true "dev": true
}, },
"node_modules/acorn": { "node_modules/acorn": {
@ -224,9 +224,9 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "16.11.26", "version": "16.11.39",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-16.11.26.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.39.tgz",
"integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==", "integrity": "sha512-K0MsdV42vPwm9L6UwhIxMAOmcvH/1OoVkZyCgEtVu4Wx7sElGloy/W7kMBNe/oJ7V/jW9BVt1F6RahH6e7tPXw==",
"dev": true "dev": true
}, },
"acorn": { "acorn": {

View File

@ -3,7 +3,7 @@
"build": "ts-node index.ts" "build": "ts-node index.ts"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^16.11.26", "@types/node": "^16.11.39",
"ts-node": "^10.2.0", "ts-node": "^10.2.0",
"tslib": "^2.3.1", "tslib": "^2.3.1",
"typescript": "^4.3.5" "typescript": "^4.3.5"

11
config_gen/tsconfig.json Normal file
View File

@ -0,0 +1,11 @@
{
"compilerOptions": {
"lib": [
"ES2021"
],
"typeRoots": [
"@types",
"node_modules/@types"
],
},
}

View File

@ -7,13 +7,22 @@ export type ConfigEntryType =
/** 只能单独房间设置 */ /** 只能单独房间设置 */
| "roomOnly" | "roomOnly"
export type ConfigValueType =
"string?"
| "int"
| "uint"
| "bool"
| "RecordMode"
| "CuttingMode"
| "AllowedAddressFamily"
export interface ConfigEntry { export interface ConfigEntry {
/** 名字 */ /** 名字 */
readonly name: string, readonly id: string,
/** 说明 */ /** 说明 */
readonly description: string, readonly name: string,
/** 代码类型 */ /** 代码类型 */
readonly type: string, readonly type: ConfigValueType,
/** 设置类型 */ /** 设置类型 */
readonly configType: ConfigEntryType readonly configType: ConfigEntryType
/** Web API 只读属性 */ /** Web API 只读属性 */
@ -21,11 +30,5 @@ export interface ConfigEntry {
/** 是否为高级设置(隐藏设置),默认为 false */ /** 是否为高级设置(隐藏设置),默认为 false */
readonly advancedConfig?: boolean, readonly advancedConfig?: boolean,
/** 默认值 */ /** 默认值 */
readonly defaultValue: string, readonly default: string | number | boolean,
/** 文档显示用默认值,默认使用 defaultValue */ }
readonly defaultValueDescription?: string,
/** XML 注释,默认使用 description */
readonly xmlComment?: string,
/** Markdown 格式的说明文档 */
readonly markdown: string,
}

View File

@ -1,5 +1,15 @@
import { ConfigEntry } from "./types";
export function trimEnd(text: string, trimChar: string): string { export function trimEnd(text: string, trimChar: string): string {
return text.slice(-1) === trimChar return text.slice(-1) === trimChar
? text.slice(0, -1) ? text.slice(0, -1)
: text; : text;
} }
export function getConfigDefaultValueText(config: ConfigEntry): string {
if (config.type != "string?") {
return config.default.toString();
} else {
return `@"${config.default.toString().replaceAll('"', '""')}"`;
}
}