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>();
/// <summary>
/// 录制文件写入结束 Webhook 地址 每行一个
/// WebhookV1
/// </summary>
public string? WebHookUrls => this.GetPropertyValue<string>();
/// <summary>
/// Webhook v2 地址 每行一个
/// WebhookV2
/// </summary>
public string? WebHookUrlsV2 => this.GetPropertyValue<string>();
@ -122,17 +122,17 @@ namespace BililiveRecorder.Core.Config.V3
public bool WpfShowTitleAndArea => this.GetPropertyValue<bool>();
/// <summary>
/// 请求 API 时使用的 Cookie
/// Cookie
/// </summary>
public string? Cookie => this.GetPropertyValue<string>();
/// <summary>
/// 替换 api.live.bilibili.com 服务器为其他反代,可以支持在云服务器上录制
/// API Host
/// </summary>
public string? LiveApiHost => this.GetPropertyValue<string>();
/// <summary>
/// HTTP API 检查时间间隔 秒
/// 主动检查时间间隔 秒
/// </summary>
public uint TimingCheckInterval => this.GetPropertyValue<uint>();
@ -157,12 +157,12 @@ namespace BililiveRecorder.Core.Config.V3
public uint TimingDanmakuRetry => this.GetPropertyValue<uint>();
/// <summary>
/// 最大允许未收到直播数据时间 毫秒
/// 最大未收到直播数据时间 毫秒
/// </summary>
public uint TimingWatchdogTimeout => this.GetPropertyValue<uint>();
/// <summary>
/// 触发 <see cref="System.Xml.XmlWriter.Flush"/> 的弹幕个数
/// 触发刷新弹幕写入缓冲的个数
/// </summary>
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)); }
/// <summary>
/// 录制文件写入结束 Webhook 地址 每行一个
/// WebhookV1
/// </summary>
public string? WebHookUrls { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); }
public bool HasWebHookUrls { get => this.GetPropertyHasValue(nameof(this.WebHookUrls)); set => this.SetPropertyHasValue<string>(value, nameof(this.WebHookUrls)); }
@ -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)); }
/// <summary>
/// Webhook v2 地址 每行一个
/// WebhookV2
/// </summary>
public string? WebHookUrlsV2 { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); }
public bool HasWebHookUrlsV2 { get => this.GetPropertyHasValue(nameof(this.WebHookUrlsV2)); set => this.SetPropertyHasValue<string>(value, nameof(this.WebHookUrlsV2)); }
@ -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)); }
/// <summary>
/// 请求 API 时使用的 Cookie
/// Cookie
/// </summary>
public string? Cookie { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); }
public bool HasCookie { get => this.GetPropertyHasValue(nameof(this.Cookie)); set => this.SetPropertyHasValue<string>(value, nameof(this.Cookie)); }
@ -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)); }
/// <summary>
/// 替换 api.live.bilibili.com 服务器为其他反代,可以支持在云服务器上录制
/// API Host
/// </summary>
public string? LiveApiHost { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); }
public bool HasLiveApiHost { get => this.GetPropertyHasValue(nameof(this.LiveApiHost)); set => this.SetPropertyHasValue<string>(value, nameof(this.LiveApiHost)); }
@ -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)); }
/// <summary>
/// HTTP API 检查时间间隔 秒
/// 主动检查时间间隔 秒
/// </summary>
public uint TimingCheckInterval { get => this.GetPropertyValue<uint>(); set => this.SetPropertyValue(value); }
public bool HasTimingCheckInterval { get => this.GetPropertyHasValue(nameof(this.TimingCheckInterval)); set => this.SetPropertyHasValue<uint>(value, nameof(this.TimingCheckInterval)); }
@ -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)); }
/// <summary>
/// 最大允许未收到直播数据时间 毫秒
/// 最大未收到直播数据时间 毫秒
/// </summary>
public uint TimingWatchdogTimeout { get => this.GetPropertyValue<uint>(); set => this.SetPropertyValue(value); }
public bool HasTimingWatchdogTimeout { get => this.GetPropertyHasValue(nameof(this.TimingWatchdogTimeout)); set => this.SetPropertyHasValue<uint>(value, nameof(this.TimingWatchdogTimeout)); }
@ -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)); }
/// <summary>
/// 触发 <see cref="System.Xml.XmlWriter.Flush"/> 的弹幕个数
/// 触发刷新弹幕写入缓冲的个数
/// </summary>
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)); }
@ -409,31 +409,31 @@ namespace BililiveRecorder.Core.Config.V3
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 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 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;
@ -441,7 +441,7 @@ namespace BililiveRecorder.Core.Config.V3
public AllowedAddressFamily NetworkTransportAllowedAddressFamily => AllowedAddressFamily.Any;
public string UserScript => string.Empty;
public string UserScript => @"";
}

View File

@ -7,8 +7,8 @@
"additionalProperties": false,
"properties": {
"FileNameRecordTemplate": {
"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 新的文件名模板系统的文档还没有写",
"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\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -24,7 +24,7 @@
},
"WebHookUrls": {
"description": "WebhookV1\n默认: ",
"markdownDescription": "WebhookV1 \n默认: ` `\n\n具体文档见 [Webhook](/docs/basic/webhook/)",
"markdownDescription": "WebhookV1 \n默认: ` `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -40,7 +40,7 @@
},
"WebHookUrlsV2": {
"description": "WebhookV2\n默认: ",
"markdownDescription": "WebhookV2 \n默认: ` `\n\n具体文档见 [Webhook](/docs/basic/webhook/)",
"markdownDescription": "WebhookV2 \n默认: ` `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -56,7 +56,7 @@
},
"WpfShowTitleAndArea": {
"description": "在界面显示标题和分区\n默认: true",
"markdownDescription": "在界面显示标题和分区 \n默认: `true `\n\n只在桌面版WPF版有效",
"markdownDescription": "在界面显示标题和分区 \n默认: `true `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -71,8 +71,8 @@
}
},
"Cookie": {
"description": "请求 API 时使用的 Cookie\n默认: (空字符串)",
"markdownDescription": "请求 API 时使用的 Cookie \n默认: `(空字符串) `\n\n",
"description": "Cookie\n默认: ",
"markdownDescription": "Cookie \n默认: ` `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -88,8 +88,8 @@
}
},
"LiveApiHost": {
"description": "请求的 API Host\n默认: https://api.live.bilibili.com",
"markdownDescription": "请求的 API Host \n默认: `https://api.live.bilibili.com `\n\n",
"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": {
@ -104,8 +104,8 @@
}
},
"TimingCheckInterval": {
"description": "HTTP API 检查时间间隔 秒\n默认: 600 (10分)",
"markdownDescription": "HTTP API 检查时间间隔 秒 \n默认: `600 (10分) `\n\n",
"description": "主动检查时间间隔 秒\n默认: 600",
"markdownDescription": "主动检查时间间隔 秒 \n默认: `600 `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -122,8 +122,8 @@
}
},
"TimingStreamRetry": {
"description": "录制断开重连时间间隔 毫秒\n默认: 6000 (6秒)",
"markdownDescription": "录制断开重连时间间隔 毫秒 \n默认: `6000 (6秒) `\n\n",
"description": "录制断开重连时间间隔 毫秒\n默认: 6000",
"markdownDescription": "录制断开重连时间间隔 毫秒 \n默认: `6000 `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -140,8 +140,8 @@
}
},
"TimingStreamRetryNoQn": {
"description": "录制无指定画质重连时间间隔 秒\n默认: 90 (1.5分钟)",
"markdownDescription": "录制无指定画质重连时间间隔 秒 \n默认: `90 (1.5分钟) `\n\n",
"description": "录制无指定画质重连时间间隔 秒\n默认: 90",
"markdownDescription": "录制无指定画质重连时间间隔 秒 \n默认: `90 `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -158,8 +158,8 @@
}
},
"TimingStreamConnect": {
"description": "连接直播服务器超时时间 毫秒\n默认: 5000 (5秒)",
"markdownDescription": "连接直播服务器超时时间 毫秒 \n默认: `5000 (5秒) `\n\n",
"description": "连接直播服务器超时时间 毫秒\n默认: 5000",
"markdownDescription": "连接直播服务器超时时间 毫秒 \n默认: `5000 `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -176,8 +176,8 @@
}
},
"TimingDanmakuRetry": {
"description": "弹幕服务器重连时间间隔 毫秒\n默认: 9000 (9秒)",
"markdownDescription": "弹幕服务器重连时间间隔 毫秒 \n默认: `9000 (9秒) `\n\n",
"description": "弹幕服务器重连时间间隔 毫秒\n默认: 9000",
"markdownDescription": "弹幕服务器重连时间间隔 毫秒 \n默认: `9000 `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -194,8 +194,8 @@
}
},
"TimingWatchdogTimeout": {
"description": "最大允许未收到直播数据时间 毫秒\n默认: 10000 (10秒)",
"markdownDescription": "最大允许未收到直播数据时间 毫秒 \n默认: `10000 (10秒) `\n\n",
"description": "最大未收到直播数据时间 毫秒\n默认: 10000",
"markdownDescription": "最大未收到直播数据时间 毫秒 \n默认: `10000 `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -256,8 +256,15 @@
"default": true
},
"Value": {
"type": "AllowedAddressFamily",
"default": "AllowedAddressFamily.Any"
"type": "integer",
"default": 0,
"enum": [
-1,
0,
1,
2
],
"description": "-1: 由系统决定\n0: 任意 IPv4 或 IPv6\n1: 仅 IPv4\n2: IPv6"
}
}
},
@ -279,7 +286,7 @@
},
"RecordMode": {
"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",
"additionalProperties": false,
"properties": {
@ -300,7 +307,7 @@
},
"CuttingMode": {
"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",
"additionalProperties": false,
"properties": {
@ -322,7 +329,7 @@
},
"CuttingNumber": {
"description": "自动分段数值\n默认: 100",
"markdownDescription": "自动分段数值 \n默认: `100 `\n\n根据 CuttingMode 设置的不同: \n当按时长分段时本设置的单位为分钟。 \n当按大小分段时本设置的单位为MiB。",
"markdownDescription": "自动分段数值 \n默认: `100 `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -340,7 +347,7 @@
},
"RecordDanmaku": {
"description": "弹幕录制\n默认: false",
"markdownDescription": "弹幕录制 \n默认: `false `\n\n是否录制弹幕,`true` 为录制,`false` 为不录制。\n\n本设置同时是所有“弹幕录制”的总开关当本设置为 `false` 时其他所有“弹幕录制”设置无效不会写入弹幕XML文件。",
"markdownDescription": "弹幕录制 \n默认: `false `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -356,7 +363,7 @@
},
"RecordDanmakuRaw": {
"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",
"additionalProperties": false,
"properties": {
@ -372,7 +379,7 @@
},
"RecordDanmakuSuperChat": {
"description": "弹幕录制-SuperChat\n默认: true",
"markdownDescription": "弹幕录制-SuperChat \n默认: `true `\n\n是否记录 SuperChat。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
"markdownDescription": "弹幕录制-SuperChat \n默认: `true `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -388,7 +395,7 @@
},
"RecordDanmakuGift": {
"description": "弹幕录制-礼物\n默认: false",
"markdownDescription": "弹幕录制-礼物 \n默认: `false `\n\n是否记录礼物。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
"markdownDescription": "弹幕录制-礼物 \n默认: `false `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -404,7 +411,7 @@
},
"RecordDanmakuGuard": {
"description": "弹幕录制-上船\n默认: true",
"markdownDescription": "弹幕录制-上船 \n默认: `true `\n\n是否记录上船(购买舰长)。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
"markdownDescription": "弹幕录制-上船 \n默认: `true `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -420,7 +427,7 @@
},
"RecordingQuality": {
"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",
"additionalProperties": false,
"properties": {
@ -441,8 +448,8 @@
"additionalProperties": false,
"properties": {
"RoomId": {
"description": "房间号\n默认: default",
"markdownDescription": "房间号 \n默认: `default `\n\n",
"description": "房间号\n默认: ",
"markdownDescription": "房间号 \n默认: ` `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -453,13 +460,14 @@
"Value": {
"type": "integer",
"minimum": -2147483648,
"maximum": 2147483647
"maximum": 2147483647,
"default": ""
}
}
},
"AutoRecord": {
"description": "自动录制\n默认: default",
"markdownDescription": "自动录制 \n默认: `default `\n\n设为 `true` 为启用自动录制,`false` 为不自动录制。",
"description": "自动录制\n默认: ",
"markdownDescription": "自动录制 \n默认: ` `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -468,13 +476,14 @@
"default": true
},
"Value": {
"type": "boolean"
"type": "boolean",
"default": ""
}
}
},
"RecordMode": {
"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",
"additionalProperties": false,
"properties": {
@ -495,7 +504,7 @@
},
"CuttingMode": {
"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",
"additionalProperties": false,
"properties": {
@ -517,7 +526,7 @@
},
"CuttingNumber": {
"description": "自动分段数值\n默认: 100",
"markdownDescription": "自动分段数值 \n默认: `100 `\n\n根据 CuttingMode 设置的不同: \n当按时长分段时本设置的单位为分钟。 \n当按大小分段时本设置的单位为MiB。",
"markdownDescription": "自动分段数值 \n默认: `100 `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -535,7 +544,7 @@
},
"RecordDanmaku": {
"description": "弹幕录制\n默认: false",
"markdownDescription": "弹幕录制 \n默认: `false `\n\n是否录制弹幕,`true` 为录制,`false` 为不录制。\n\n本设置同时是所有“弹幕录制”的总开关当本设置为 `false` 时其他所有“弹幕录制”设置无效不会写入弹幕XML文件。",
"markdownDescription": "弹幕录制 \n默认: `false `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -551,7 +560,7 @@
},
"RecordDanmakuRaw": {
"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",
"additionalProperties": false,
"properties": {
@ -567,7 +576,7 @@
},
"RecordDanmakuSuperChat": {
"description": "弹幕录制-SuperChat\n默认: true",
"markdownDescription": "弹幕录制-SuperChat \n默认: `true `\n\n是否记录 SuperChat。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
"markdownDescription": "弹幕录制-SuperChat \n默认: `true `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -583,7 +592,7 @@
},
"RecordDanmakuGift": {
"description": "弹幕录制-礼物\n默认: false",
"markdownDescription": "弹幕录制-礼物 \n默认: `false `\n\n是否记录礼物。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
"markdownDescription": "弹幕录制-礼物 \n默认: `false `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -599,7 +608,7 @@
},
"RecordDanmakuGuard": {
"description": "弹幕录制-上船\n默认: true",
"markdownDescription": "弹幕录制-上船 \n默认: `true `\n\n是否记录上船(购买舰长)。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
"markdownDescription": "弹幕录制-上船 \n默认: `true `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
@ -615,7 +624,7 @@
},
"RecordingQuality": {
"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",
"additionalProperties": false,
"properties": {
@ -641,10 +650,10 @@
"properties": {
"$schema": {
"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": {
"const": 2
"const": 3
},
"global": {
"$ref": "#/definitions/global-config"

View File

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

View File

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

View File

@ -1,49 +1,42 @@
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) {
function mapTypeToJsonSchema(id: string, type: string, defaultValue: any) {
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 "AllowedAddressFamily":
return { type: "integer", default: 0, enum: [-1, 0, 1, 2], "description": "-1: 由系统决定\n0: 任意 IPv4 或 IPv6\n1: 仅 IPv4\n2: IPv6" };
case "uint":
return { type: "integer", minimum: 0, maximum: 4294967295, default: tryEvalValue(defaultValue) };
return { type: "integer", minimum: 0, maximum: 4294967295, default: defaultValue };
case "int":
return { type: "integer", minimum: -2147483648, maximum: 2147483647, default: tryEvalValue(defaultValue) };
return { type: "integer", minimum: -2147483648, maximum: 2147483647, default: defaultValue };
case "bool":
return { type: "boolean", default: tryEvalValue(defaultValue) };
return { type: "boolean", default: defaultValue };
case "string":
case "string?":
if (name === 'Cookie') {
if (id === 'Cookie') {
return { type: "string", pattern: "^(\S+=\S+;? ?)*$", maxLength: 4096, };
}
return { type: "string", default: mapCSharpString(defaultValue) };
return { type: "string", default: defaultValue };
default:
return { type, default: defaultValue };
}
}
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'];
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,
target[config.id] = {
description: config.name
+ '\n默认: ' + config.default,
markdownDescription: config.name
+ ' \n默认: `' + config.default
+ ' `\n\n',
type: "object",
additionalProperties: false,
properties: {
@ -89,10 +82,10 @@ export default function (data: ConfigEntry[]): string {
"properties": {
"$schema": {
"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": {
"const": 2
"const": 3
},
"global": {
"$ref": "#/definitions/global-config"

View File

@ -10,41 +10,41 @@ using HierarchicalPropertyDefault;
`;
function write_query_graphType_property(r: ConfigEntry) {
if (r.configType == "roomOnly") {
result += `this.Field(x => x.${r.name});\n`;
result += `this.Field(x => x.${r.id});\n`;
} 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) {
if (r.configType == "roomOnly") {
result += `public ${r.type} ${r.name} { get; set; }\n`;
result += `public ${r.type} ${r.id} { get; set; }\n`;
} 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) {
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 {
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) {
if (r.configType == "roomOnly") {
result += `public ${r.type}? ${r.name} { get; set; }\n`;
result += `public ${r.type}? ${r.id} { get; set; }\n`;
} 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) {
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 {
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")
.forEach(r => {
result += `this.Field(x => x.${r.name});\n`;
result += `this.Field(x => x.${r.id});\n`;
});
result += "}\n}\n\n";

View File

@ -1,61 +1,26 @@
import { ConfigEntry } from "../types"
import fs from "fs"
import { statSync, writeFileSync } 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'))) {
if (!statSync(resolve(path, 'mkdocs.yml'))) {
console.error('Check your path');
return;
}
if (!fs.statSync(resolve(path, 'index.html'))) {
if (!statSync(resolve(path, 'docs/user/settings.md'))) {
console.error('Check your path');
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 {
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'
// 高级设置说明
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;
function buildJson(data: ConfigEntry[]): string {
return JSON.stringify(data, null, 2);
}

View File

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

View File

@ -3,7 +3,7 @@
"build": "ts-node index.ts"
},
"devDependencies": {
"@types/node": "^16.11.26",
"@types/node": "^16.11.39",
"ts-node": "^10.2.0",
"tslib": "^2.3.1",
"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"
export type ConfigValueType =
"string?"
| "int"
| "uint"
| "bool"
| "RecordMode"
| "CuttingMode"
| "AllowedAddressFamily"
export interface ConfigEntry {
/** 名字 */
readonly name: string,
readonly id: string,
/** 说明 */
readonly description: string,
readonly name: string,
/** 代码类型 */
readonly type: string,
readonly type: ConfigValueType,
/** 设置类型 */
readonly configType: ConfigEntryType
/** Web API 只读属性 */
@ -21,11 +30,5 @@ export interface ConfigEntry {
/** 是否为高级设置(隐藏设置),默认为 false */
readonly advancedConfig?: boolean,
/** 默认值 */
readonly defaultValue: string,
/** 文档显示用默认值,默认使用 defaultValue */
readonly defaultValueDescription?: string,
/** XML 注释,默认使用 description */
readonly xmlComment?: string,
/** Markdown 格式的说明文档 */
readonly markdown: string,
}
readonly default: string | number | boolean,
}

View File

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