mirror of
https://github.com/BililiveRecorder/BililiveRecorder.git
synced 2024-11-15 19:22:19 +08:00
Improve config code generation
This commit is contained in:
parent
2521ed228e
commit
ca3dfd06bf
12
.config/dotnet-tools.json
Normal file
12
.config/dotnet-tools.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-format": {
|
||||
"version": "5.1.225507",
|
||||
"commands": [
|
||||
"dotnet-format"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
81
.tools/build_config.js
Normal file
81
.tools/build_config.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
"use strict";
|
||||
import { spawn } from "child_process";
|
||||
import { stdout, stderr } from "process";
|
||||
import { writeFileSync } from "fs";
|
||||
import { resolve, dirname } from "path";
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
import data from "./config_data.js"
|
||||
|
||||
import generate_json_schema from "./generate_json_schema.js"
|
||||
import generate_core_config from "./generate_core_config.js"
|
||||
import generate_web_config from "./generate_web_config.js"
|
||||
|
||||
const baseDirectory = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const DO_NOT_EDIT_COMMENT = `// ******************************
|
||||
// GENERATED CODE, DO NOT EDIT MANUALLY.
|
||||
// SEE .tools/build_config.js
|
||||
// ******************************\n\n`
|
||||
|
||||
// ---------------------------------------------
|
||||
// SCHEMA
|
||||
// ---------------------------------------------
|
||||
|
||||
console.log("[node] writing json schema...")
|
||||
|
||||
const json_schema_path = resolve(baseDirectory, '../configV2.schema.json');
|
||||
|
||||
const json_schema_code = generate_json_schema(data);
|
||||
|
||||
writeFileSync(json_schema_path, json_schema_code, {
|
||||
encoding: "utf8"
|
||||
});
|
||||
|
||||
// ---------------------------------------------
|
||||
// CORE
|
||||
// ---------------------------------------------
|
||||
|
||||
console.log("[node] writing core config...")
|
||||
|
||||
const core_config_path = resolve(baseDirectory, '../BililiveRecorder.Core/Config/V2/Config.gen.cs');
|
||||
|
||||
const core_config_code = generate_core_config(data);
|
||||
|
||||
writeFileSync(core_config_path, DO_NOT_EDIT_COMMENT + core_config_code, {
|
||||
encoding: "utf8"
|
||||
});
|
||||
|
||||
// ---------------------------------------------
|
||||
// WEB
|
||||
// ---------------------------------------------
|
||||
/* disabled
|
||||
console.log("[node] writing web config...")
|
||||
|
||||
const web_config_path = resolve(baseDirectory, '../BililiveRecorder.Web.Schemas/Types/Config.gen.cs');
|
||||
|
||||
const web_config_code = generate_web_config(data);
|
||||
|
||||
writeFileSync(web_config_path, DO_NOT_EDIT_COMMENT + web_config_code, {
|
||||
encoding: "utf8"
|
||||
});
|
||||
*/
|
||||
// ---------------------------------------------
|
||||
// FORMAT
|
||||
// ---------------------------------------------
|
||||
|
||||
console.log("[node] formatting...")
|
||||
|
||||
let format = spawn('dotnet', ['tool', 'run', 'dotnet-format', '--', '--include', './BililiveRecorder.Core/Config/V2/Config.gen.cs'/*, './BililiveRecorder.Web.Schemas/Types/Config.gen.cs'*/])
|
||||
|
||||
format.stdout.on('data', function (data) {
|
||||
stdout.write('[dotnet-format] ' + data.toString());
|
||||
});
|
||||
|
||||
format.stderr.on('data', function (data) {
|
||||
stderr.write('[dotnet-format] ' + data.toString());
|
||||
});
|
||||
|
||||
format.on('exit', function (code) {
|
||||
console.log('[node] format done code ' + code.toString());
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
module.exports = {
|
||||
export default {
|
||||
"global": [{
|
||||
"name": "TimingStreamRetry",
|
||||
"type": "uint",
|
||||
|
@ -70,13 +70,14 @@ module.exports = {
|
|||
"type": "int",
|
||||
"desc": "房间号",
|
||||
"default": "default",
|
||||
"without_global": true
|
||||
"without_global": true,
|
||||
"web_readonly": true
|
||||
}, {
|
||||
"name": "AutoRecord",
|
||||
"type": "bool",
|
||||
"desc": "是否启用自动录制",
|
||||
"default": "default",
|
||||
"without_global": true
|
||||
"without_global": true,
|
||||
}, {
|
||||
"name": "RecordMode",
|
||||
"type": "RecordMode",
|
66
.tools/generate_core_config.js
Normal file
66
.tools/generate_core_config.js
Normal file
|
@ -0,0 +1,66 @@
|
|||
"use strict";
|
||||
|
||||
export default function generate_core_config(data) {
|
||||
let result = `using System.ComponentModel;
|
||||
using HierarchicalPropertyDefault;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
#nullable enable
|
||||
namespace BililiveRecorder.Core.Config.V2
|
||||
{
|
||||
`;
|
||||
|
||||
function write_property(r) {
|
||||
result += `/// <summary>\n/// ${r.desc}\n/// </summary>\n`;
|
||||
result += `public ${r.type}${!!r.nullable ? "?" : ""} ${r.name} { get => this.GetPropertyValue<${r.type}>(); set => this.SetPropertyValue(value); }\n`;
|
||||
result += `public bool Has${r.name} { get => this.GetPropertyHasValue(nameof(this.${r.name})); set => this.SetPropertyHasValue<${r.type}>(value, nameof(this.${r.name})); }\n`;
|
||||
result += `[JsonProperty(nameof(${r.name})), EditorBrowsable(EditorBrowsableState.Never)]\n`;
|
||||
result += `public Optional<${r.type}${!!r.nullable ? "?" : ""}> Optional${r.name} { get => this.GetPropertyValueOptional<${r.type}>(nameof(this.${r.name})); set => this.SetPropertyValueOptional(value, nameof(this.${r.name})); }\n\n`;
|
||||
}
|
||||
|
||||
function write_readonly_property(r) {
|
||||
result += `/// <summary>\n/// ${r.desc}\n/// </summary>\n`;
|
||||
result += `public ${r.type}${!!r.nullable ? "?" : ""} ${r.name} => this.GetPropertyValue<${r.type}>();\n\n`;
|
||||
}
|
||||
|
||||
{
|
||||
result += "[JsonObject(MemberSerialization.OptIn)]\n";
|
||||
result += "public sealed partial class RoomConfig : HierarchicalObject<GlobalConfig, RoomConfig>\n";
|
||||
result += "{\n";
|
||||
|
||||
data.room.forEach(r => write_property(r));
|
||||
data.global.forEach(r => write_readonly_property(r));
|
||||
|
||||
result += "}\n\n";
|
||||
}
|
||||
|
||||
{
|
||||
result += "[JsonObject(MemberSerialization.OptIn)]\n";
|
||||
result += "public sealed partial class GlobalConfig : HierarchicalObject<DefaultConfig, GlobalConfig>\n";
|
||||
result += "{\n";
|
||||
|
||||
data.global
|
||||
.concat(data.room.filter(x => !x.without_global))
|
||||
.forEach(r => write_property(r));
|
||||
|
||||
result += "}\n\n";
|
||||
}
|
||||
|
||||
{
|
||||
result += `public sealed partial class DefaultConfig
|
||||
{
|
||||
public static readonly DefaultConfig Instance = new DefaultConfig();
|
||||
private DefaultConfig() {}\n\n`;
|
||||
|
||||
data.global
|
||||
.concat(data.room.filter(x => !x.without_global))
|
||||
.forEach(r => {
|
||||
result += `public ${r.type} ${r.name} => ${r.default};\n\n`;
|
||||
});
|
||||
|
||||
result += "}\n\n";
|
||||
}
|
||||
|
||||
result += `}\n`;
|
||||
return result;
|
||||
}
|
95
.tools/generate_json_schema.js
Normal file
95
.tools/generate_json_schema.js
Normal file
|
@ -0,0 +1,95 @@
|
|||
function tryEvalValue(str) {
|
||||
try {
|
||||
return eval(str);
|
||||
} catch {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
function mapTypeToJsonSchema(name, type, defVal) {
|
||||
switch (type) {
|
||||
case "RecordMode":
|
||||
return { type: "integer", default: 0, enum: [0, 1], "description": "0: Standard\n1: Raw" };
|
||||
case "CuttingMode":
|
||||
return { type: "integer", default: 0, enum: [0, 1, 2], "description": "0: 禁用\n1: 根据时间切割\n2: 根据文件大小切割" };
|
||||
case "uint":
|
||||
return { type: "integer", minimum: 0, maximum: 4294967295, default: tryEvalValue(defVal) };
|
||||
case "int":
|
||||
return { type: "integer", minimum: -2147483648, maximum: 2147483647, default: tryEvalValue(defVal) };
|
||||
case "bool":
|
||||
return { type: "boolean", default: tryEvalValue(defVal) };
|
||||
case "string":
|
||||
if (name === 'Cookie') {
|
||||
return { type: "string", pattern: "^(\S+=\S+;? ?)*$", maxLength: 4096, };
|
||||
}
|
||||
return { type: "string", default: defVal === 'string.Empty' ? '' : tryEvalValue(defVal.replace(/^@/, '')) };
|
||||
default:
|
||||
return { type, default: defVal };
|
||||
}
|
||||
}
|
||||
|
||||
function insert(target, { name, type, desc, default: defVal/*, nullable */ }) {
|
||||
const typeObj = mapTypeToJsonSchema(name, type, defVal);
|
||||
if (defVal === 'default') delete typeObj['default'];
|
||||
target[name] = {
|
||||
"description": desc,
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": { "type": "boolean", "default": true }, "Value": typeObj
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default function generate_json_schema(data) {
|
||||
const sharedConfig = {};
|
||||
const globalConfig = {};
|
||||
const roomConfig = {};
|
||||
|
||||
data.room.filter(x => !x.without_global).forEach(v => insert(sharedConfig, v));
|
||||
data.room.filter(x => x.without_global).forEach(v => insert(roomConfig, v));
|
||||
data.global.forEach(v => insert(globalConfig, v));
|
||||
|
||||
const schema = {
|
||||
"$comment": "GENERATED CODE, DO NOT EDIT MANUALLY.",
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"definitions": {
|
||||
"global-config": {
|
||||
"description": "全局配置",
|
||||
"additionalProperties": false,
|
||||
"properties": { ...globalConfig, ...sharedConfig }
|
||||
},
|
||||
"room-config": {
|
||||
"description": "单个房间配置",
|
||||
"additionalProperties": false,
|
||||
"properties": { ...roomConfig, ...sharedConfig }
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"$schema",
|
||||
"version"
|
||||
],
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"default": "https://raw.githubusercontent.com/Bililive/BililiveRecorder/dev-1.3/configV2.schema.json"
|
||||
},
|
||||
"version": {
|
||||
"const": 2
|
||||
},
|
||||
"global": {
|
||||
"$ref": "#/definitions/global-config"
|
||||
},
|
||||
"rooms": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/room-config"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify(schema, null, 2)
|
||||
}
|
134
.tools/generate_web_config.js
Normal file
134
.tools/generate_web_config.js
Normal file
|
@ -0,0 +1,134 @@
|
|||
"use strict";
|
||||
|
||||
export default function generate_web_config(data) {
|
||||
let result = `using BililiveRecorder.Core.Config.V2;
|
||||
using GraphQL.Types;
|
||||
using HierarchicalPropertyDefault;
|
||||
|
||||
#nullable enable
|
||||
namespace BililiveRecorder.Web.Schemas.Types
|
||||
{
|
||||
`;
|
||||
|
||||
function write_query_graphType_property(r) {
|
||||
if (r.without_global) {
|
||||
result += `this.Field(x => x.${r.name});\n`;
|
||||
} else {
|
||||
result += `this.Field(x => x.Optional${r.name}, type: typeof(HierarchicalOptionalType<${r.type}>));\n`;
|
||||
}
|
||||
}
|
||||
|
||||
function write_mutation_graphType_property(r) {
|
||||
if (r.without_global) {
|
||||
result += `this.Field(x => x.${r.name}, nullable: true);\n`;
|
||||
} else {
|
||||
result += `this.Field(x => x.Optional${r.name}, nullable: true, type: typeof(HierarchicalOptionalInputType<${r.type}>));\n`;
|
||||
}
|
||||
}
|
||||
|
||||
function write_mutation_dataType_property(r) {
|
||||
if (r.without_global) {
|
||||
result += `public ${r.type + (r.nullable ? '?' : '')}? ${r.name} { get; set; }\n`;
|
||||
} else {
|
||||
result += `public Optional<${r.type + (r.nullable ? '?' : '')}>? Optional${r.name} { get; set; }\n`;
|
||||
}
|
||||
}
|
||||
|
||||
function write_mutation_apply_method(r) {
|
||||
if (r.without_global) {
|
||||
result += `if (this.${r.name}.HasValue) config.${r.name} = this.${r.name}.Value;\n`;
|
||||
} else {
|
||||
result += `if (this.Optional${r.name}.HasValue) config.Optional${r.name} = this.Optional${r.name}.Value;\n`;
|
||||
}
|
||||
}
|
||||
|
||||
{ // ====== RoomConfigType ======
|
||||
result += "internal class RoomConfigType : ObjectGraphType<RoomConfig>\n{\n";
|
||||
result += "public RoomConfigType()\n{\n"
|
||||
|
||||
data.room.forEach(r => write_query_graphType_property(r));
|
||||
|
||||
result += "}\n}\n\n";
|
||||
}
|
||||
|
||||
{ // ====== GlobalConfigType ======
|
||||
result += "internal class GlobalConfigType : ObjectGraphType<GlobalConfig>\n{\n"
|
||||
result += "public GlobalConfigType()\n{\n";
|
||||
|
||||
data.global
|
||||
.concat(data.room.filter(x => !x.without_global))
|
||||
.forEach(r => write_query_graphType_property(r));
|
||||
|
||||
result += "}\n}\n\n";
|
||||
}
|
||||
|
||||
{ // ====== DefaultConfigType ======
|
||||
result += "internal class DefaultConfigType : ObjectGraphType<DefaultConfig>\n{\n"
|
||||
result += "public DefaultConfigType()\n{\n";
|
||||
|
||||
data.global
|
||||
.concat(data.room.filter(x => !x.without_global))
|
||||
.forEach(r => {
|
||||
result += `this.Field(x => x.${r.name});\n`;
|
||||
});
|
||||
|
||||
result += "}\n}\n\n";
|
||||
}
|
||||
|
||||
{ // ====== SetRoomConfig ======
|
||||
result += "internal class SetRoomConfig\n{\n"
|
||||
|
||||
data.room.filter(x => !x.web_readonly)
|
||||
.forEach(r => write_mutation_dataType_property(r));
|
||||
|
||||
result += "\npublic void ApplyTo(RoomConfig config)\n{\n";
|
||||
|
||||
data.room.filter(x => !x.web_readonly)
|
||||
.forEach(r => write_mutation_apply_method(r));
|
||||
|
||||
result += "}\n}\n\n";
|
||||
}
|
||||
|
||||
{ // ====== SetRoomConfigType ======
|
||||
result += "internal class SetRoomConfigType : InputObjectGraphType<SetRoomConfig>\n{\n"
|
||||
result += "public SetRoomConfigType()\n{\n";
|
||||
|
||||
data.room.filter(x => !x.web_readonly)
|
||||
.forEach(r => write_mutation_graphType_property(r));
|
||||
|
||||
result += "}\n}\n\n";
|
||||
}
|
||||
|
||||
{ // ====== SetGlobalConfig ======
|
||||
result += "internal class SetGlobalConfig\n{\n"
|
||||
|
||||
data.global
|
||||
.concat(data.room.filter(x => !x.without_global))
|
||||
.filter(x => !x.web_readonly)
|
||||
.forEach(r => write_mutation_dataType_property(r));
|
||||
|
||||
result += "\npublic void ApplyTo(GlobalConfig config)\n{\n";
|
||||
|
||||
data.global
|
||||
.concat(data.room.filter(x => !x.without_global))
|
||||
.filter(x => !x.web_readonly)
|
||||
.forEach(r => write_mutation_apply_method(r));
|
||||
|
||||
result += "}\n}\n\n";
|
||||
}
|
||||
|
||||
{ // ====== SetGlobalConfigType ======
|
||||
result += "internal class SetGlobalConfigType : InputObjectGraphType<SetGlobalConfig>\n{\n"
|
||||
result += "public SetGlobalConfigType()\n{\n";
|
||||
|
||||
data.global
|
||||
.concat(data.room.filter(x => !x.without_global))
|
||||
.filter(x => !x.web_readonly)
|
||||
.forEach(r => write_mutation_graphType_property(r));
|
||||
|
||||
result += "}\n}\n\n";
|
||||
}
|
||||
|
||||
result += `}\n`;
|
||||
return result;
|
||||
}
|
3
.tools/package.json
Normal file
3
.tools/package.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"type": "module"
|
||||
}
|
|
@ -135,7 +135,7 @@ namespace BililiveRecorder.Cli.Configure
|
|||
switch (selection)
|
||||
{
|
||||
case JsonSchemaSelection.Default:
|
||||
config.DollarSignSchema = "https://raw.githubusercontent.com/Bililive/BililiveRecorder/dev-1.3/BililiveRecorder.Core/Config/V2/config.schema.json";
|
||||
config.DollarSignSchema = "https://raw.githubusercontent.com/Bililive/BililiveRecorder/dev-1.3/configV2.schema.json";
|
||||
break;
|
||||
case JsonSchemaSelection.Custom:
|
||||
config.DollarSignSchema = AnsiConsole.Prompt(new TextPrompt<string>("[green]JSON Schema[/]:").AllowEmpty());
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace BililiveRecorder.Cli.Configure
|
|||
{
|
||||
public enum JsonSchemaSelection
|
||||
{
|
||||
[Description("https://raw.githubusercontent.com/.../config.schema.json")]
|
||||
[Description("https://raw.githubusercontent.com/Bililive/BililiveRecorder/dev-1.3/configV2.schema.json")]
|
||||
Default,
|
||||
|
||||
[Description("Custom")]
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
// ******************************
|
||||
// GENERATED CODE, DO NOT EDIT.
|
||||
// RUN FORMATTER AFTER GENERATE
|
||||
// GENERATED CODE, DO NOT EDIT MANUALLY.
|
||||
// SEE .tools/build_config.js
|
||||
// ******************************
|
||||
|
||||
using System.ComponentModel;
|
||||
using HierarchicalPropertyDefault;
|
||||
using Newtonsoft.Json;
|
||||
|
@ -337,7 +338,7 @@ namespace BililiveRecorder.Core.Config.V2
|
|||
|
||||
public sealed partial class DefaultConfig
|
||||
{
|
||||
internal static readonly DefaultConfig Instance = new DefaultConfig();
|
||||
public static readonly DefaultConfig Instance = new DefaultConfig();
|
||||
private DefaultConfig() { }
|
||||
|
||||
public uint TimingStreamRetry => 6 * 1000;
|
||||
|
|
|
@ -1,169 +0,0 @@
|
|||
"use strict";
|
||||
const fs = require("fs");
|
||||
const data = require("./build_config.data.js");
|
||||
|
||||
const CODE_HEADER =
|
||||
`// ******************************
|
||||
// GENERATED CODE, DO NOT EDIT.
|
||||
// RUN FORMATTER AFTER GENERATE
|
||||
// ******************************
|
||||
using System.ComponentModel;
|
||||
using HierarchicalPropertyDefault;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
#nullable enable
|
||||
namespace BililiveRecorder.Core.Config.V2
|
||||
{
|
||||
`;
|
||||
|
||||
const CODE_FOOTER = `}\n`;
|
||||
|
||||
let result = CODE_HEADER;
|
||||
|
||||
function write_property(r) {
|
||||
result += `/// <summary>\n/// ${r.desc}\n/// </summary>\n`
|
||||
result += `public ${r.type}${!!r.nullable ? "?" : ""} ${r.name} { get => this.GetPropertyValue<${r.type}>(); set => this.SetPropertyValue(value); }\n`
|
||||
result += `public bool Has${r.name} { get => this.GetPropertyHasValue(nameof(this.${r.name})); set => this.SetPropertyHasValue<${r.type}>(value, nameof(this.${r.name})); }\n`
|
||||
result += `[JsonProperty(nameof(${r.name})), EditorBrowsable(EditorBrowsableState.Never)]\n`
|
||||
result += `public Optional<${r.type}${!!r.nullable ? "?" : ""}> Optional${r.name} { get => this.GetPropertyValueOptional<${r.type}>(nameof(this.${r.name})); set => this.SetPropertyValueOptional(value, nameof(this.${r.name})); }\n\n`
|
||||
}
|
||||
|
||||
function write_readonly_property(r) {
|
||||
result += `/// <summary>\n/// ${r.desc}\n/// </summary>\n`
|
||||
result += `public ${r.type}${!!r.nullable ? "?" : ""} ${r.name} => this.GetPropertyValue<${r.type}>();\n\n`
|
||||
}
|
||||
|
||||
{
|
||||
result += "[JsonObject(MemberSerialization.OptIn)]\n"
|
||||
result += "public sealed partial class RoomConfig : HierarchicalObject<GlobalConfig, RoomConfig>\n"
|
||||
result += "{\n";
|
||||
|
||||
data.room.forEach(r => write_property(r))
|
||||
data.global.forEach(r => write_readonly_property(r))
|
||||
|
||||
result += "}\n\n"
|
||||
}
|
||||
|
||||
{
|
||||
result += "[JsonObject(MemberSerialization.OptIn)]\n"
|
||||
result += "public sealed partial class GlobalConfig : HierarchicalObject<DefaultConfig, GlobalConfig>\n"
|
||||
result += "{\n";
|
||||
|
||||
data.global
|
||||
.concat(data.room.filter(x => !x.without_global))
|
||||
.forEach(r => write_property(r))
|
||||
|
||||
result += "}\n\n"
|
||||
}
|
||||
|
||||
{
|
||||
result += `public sealed partial class DefaultConfig
|
||||
{
|
||||
internal static readonly DefaultConfig Instance = new DefaultConfig();
|
||||
private DefaultConfig() {}\n\n`;
|
||||
|
||||
data.global
|
||||
.concat(data.room.filter(x => !x.without_global))
|
||||
.forEach(r => {
|
||||
result += `public ${r.type} ${r.name} => ${r.default};\n\n`
|
||||
})
|
||||
|
||||
result += "}\n\n"
|
||||
}
|
||||
|
||||
result += CODE_FOOTER;
|
||||
|
||||
fs.writeFileSync("./Config.gen.cs", result, {
|
||||
encoding: "utf8"
|
||||
});
|
||||
|
||||
console.log("记得 format Config.gen.cs")
|
||||
|
||||
/** 进行一个json schema的生成 */
|
||||
const sharedConfig = {};
|
||||
const globalConfig = {};
|
||||
const roomConfig = {};
|
||||
function tEval(str) {
|
||||
try {
|
||||
return eval(str);
|
||||
} catch {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
function switchType(name, type, defVal) {
|
||||
switch (type) {
|
||||
case "RecordMode":
|
||||
return { type: "integer", default: 0, enum: [0, 1], "description": "0: Standard\n1: Raw" };
|
||||
case "CuttingMode":
|
||||
return { type: "integer", default: 0, enum: [0, 1, 2], "description": "0: 禁用\n1: 根据时间切割\n2: 根据文件大小切割" };
|
||||
case "uint":
|
||||
return { type: "integer", minimum: 0, maximum: 4294967295, default: tEval(defVal) };
|
||||
case "int":
|
||||
return { type: "integer", minimum: -2147483648, maximum: 2147483647, default: tEval(defVal) };
|
||||
case "bool":
|
||||
return { type: "boolean", default: tEval(defVal) };
|
||||
case "string":
|
||||
if (name === 'Cookie') {
|
||||
return { type: "string", pattern: "^(\S+=\S+;? ?)*$", maxLength: 4096, };
|
||||
}
|
||||
return { type: "string", default: defVal === 'string.Empty' ? '' : tEval(defVal.replace(/^@/, '')) };
|
||||
default:
|
||||
return { type, default: defVal };
|
||||
}
|
||||
}
|
||||
function insert(target, { name, type, desc, default: defVal/*, nullable */ }) {
|
||||
const typeObj = switchType(name, type, defVal);
|
||||
if (defVal === 'default') delete typeObj['default'];
|
||||
target[name] = {
|
||||
"description": desc,
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": { "type": "boolean", "default": true }, "Value": typeObj
|
||||
}
|
||||
};
|
||||
}
|
||||
data.room.filter(x => !x.without_global).forEach(v => insert(sharedConfig, v));
|
||||
data.room.filter(x => x.without_global).forEach(v => insert(roomConfig, v));
|
||||
data.global.forEach(v => insert(globalConfig, v));
|
||||
|
||||
|
||||
fs.writeFileSync("./config.schema.json", "// GENERATED CODE, DO NOT EDIT.\n" + JSON.stringify({
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"definitions": {
|
||||
"global-config": {
|
||||
"description": "全局配置",
|
||||
"additionalProperties": false,
|
||||
"properties": { ...sharedConfig, ...globalConfig }
|
||||
},
|
||||
"room-config": {
|
||||
"description": "单个房间配置",
|
||||
"additionalProperties": false,
|
||||
"properties": { ...sharedConfig, ...roomConfig }
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"$schema",
|
||||
"version"
|
||||
],
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"default": "https://raw.githubusercontent.com/Bililive/BililiveRecorder/dev-1.3/BililiveRecorder.Core/Config/V2/config.schema.json"
|
||||
},
|
||||
"version": {
|
||||
"const": 2
|
||||
},
|
||||
"global": {
|
||||
"$ref": "#/definitions/global-config"
|
||||
},
|
||||
"rooms": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/room-config"
|
||||
}
|
||||
}
|
||||
}
|
||||
}, null, 4));
|
|
@ -1,5 +1,5 @@
|
|||
// GENERATED CODE, DO NOT EDIT.
|
||||
{
|
||||
"$comment": "Deprecated. Use https://raw.githubusercontent.com/Bililive/BililiveRecorder/dev-1.3/configV2.schema.json",
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"definitions": {
|
||||
"global-config": {
|
||||
|
@ -542,8 +542,7 @@
|
|||
],
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"default": "https://raw.githubusercontent.com/Bililive/BililiveRecorder/dev-1.3/BililiveRecorder.Core/Config/V2/config.schema.json"
|
||||
"const": "https://raw.githubusercontent.com/Bililive/BililiveRecorder/dev-1.3/configV2.schema.json"
|
||||
},
|
||||
"version": {
|
||||
"const": 2
|
||||
|
|
561
configV2.schema.json
Normal file
561
configV2.schema.json
Normal file
|
@ -0,0 +1,561 @@
|
|||
{
|
||||
"$comment": "GENERATED CODE, DO NOT EDIT MANUALLY.",
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"definitions": {
|
||||
"global-config": {
|
||||
"description": "全局配置",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"TimingStreamRetry": {
|
||||
"description": "录制断开重连时间间隔 毫秒",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4294967295,
|
||||
"default": 6000
|
||||
}
|
||||
}
|
||||
},
|
||||
"TimingStreamConnect": {
|
||||
"description": "连接直播服务器超时时间 毫秒",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4294967295,
|
||||
"default": 5000
|
||||
}
|
||||
}
|
||||
},
|
||||
"TimingDanmakuRetry": {
|
||||
"description": "弹幕服务器重连时间间隔 毫秒",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4294967295,
|
||||
"default": 15000
|
||||
}
|
||||
}
|
||||
},
|
||||
"TimingCheckInterval": {
|
||||
"description": "HTTP API 检查时间间隔 秒",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4294967295,
|
||||
"default": 600
|
||||
}
|
||||
}
|
||||
},
|
||||
"TimingWatchdogTimeout": {
|
||||
"description": "最大未收到新直播数据时间 毫秒",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4294967295,
|
||||
"default": 10000
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmakuFlushInterval": {
|
||||
"description": "触发 <see cref=\"System.Xml.XmlWriter.Flush\"/> 的弹幕个数",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4294967295,
|
||||
"default": 20
|
||||
}
|
||||
}
|
||||
},
|
||||
"Cookie": {
|
||||
"description": "请求 API 时使用的 Cookie",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "string",
|
||||
"pattern": "^(S+=S+;? ?)*$",
|
||||
"maxLength": 4096
|
||||
}
|
||||
}
|
||||
},
|
||||
"WebHookUrls": {
|
||||
"description": "录制文件写入结束 Webhook 地址 每行一个",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"WebHookUrlsV2": {
|
||||
"description": "Webhook v2 地址 每行一个",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"LiveApiHost": {
|
||||
"description": "替换 api.live.bilibili.com 服务器为其他反代,可以支持在云服务器上录制",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "string",
|
||||
"default": "https://api.live.bilibili.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordFilenameFormat": {
|
||||
"description": "录制文件名模板",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "string",
|
||||
"default": "{roomid}-{name}/录制-{roomid}-{date}-{time}-{ms}-{title}.flv"
|
||||
}
|
||||
}
|
||||
},
|
||||
"WpfShowTitleAndArea": {
|
||||
"description": "是否显示直播间标题和分区",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordMode": {
|
||||
"description": "录制模式",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"default": 0,
|
||||
"enum": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"description": "0: Standard\n1: Raw"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CuttingMode": {
|
||||
"description": "录制文件自动切割模式",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"default": 0,
|
||||
"enum": [
|
||||
0,
|
||||
1,
|
||||
2
|
||||
],
|
||||
"description": "0: 禁用\n1: 根据时间切割\n2: 根据文件大小切割"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CuttingNumber": {
|
||||
"description": "录制文件自动切割数值(分钟/MiB)",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4294967295,
|
||||
"default": 100
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmaku": {
|
||||
"description": "是否同时录制弹幕",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmakuRaw": {
|
||||
"description": "是否记录弹幕原始数据",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmakuSuperChat": {
|
||||
"description": "是否同时录制 SuperChat",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmakuGift": {
|
||||
"description": "是否同时录制 礼物",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmakuGuard": {
|
||||
"description": "是否同时录制 上船",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordingQuality": {
|
||||
"description": "录制的直播画质 qn 值,逗号分割,靠前的优先",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "string",
|
||||
"default": "10000"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"room-config": {
|
||||
"description": "单个房间配置",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"RoomId": {
|
||||
"description": "房间号",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"minimum": -2147483648,
|
||||
"maximum": 2147483647
|
||||
}
|
||||
}
|
||||
},
|
||||
"AutoRecord": {
|
||||
"description": "是否启用自动录制",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordMode": {
|
||||
"description": "录制模式",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"default": 0,
|
||||
"enum": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"description": "0: Standard\n1: Raw"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CuttingMode": {
|
||||
"description": "录制文件自动切割模式",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"default": 0,
|
||||
"enum": [
|
||||
0,
|
||||
1,
|
||||
2
|
||||
],
|
||||
"description": "0: 禁用\n1: 根据时间切割\n2: 根据文件大小切割"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CuttingNumber": {
|
||||
"description": "录制文件自动切割数值(分钟/MiB)",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4294967295,
|
||||
"default": 100
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmaku": {
|
||||
"description": "是否同时录制弹幕",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmakuRaw": {
|
||||
"description": "是否记录弹幕原始数据",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmakuSuperChat": {
|
||||
"description": "是否同时录制 SuperChat",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmakuGift": {
|
||||
"description": "是否同时录制 礼物",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmakuGuard": {
|
||||
"description": "是否同时录制 上船",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordingQuality": {
|
||||
"description": "录制的直播画质 qn 值,逗号分割,靠前的优先",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "string",
|
||||
"default": "10000"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"$schema",
|
||||
"version"
|
||||
],
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"default": "https://raw.githubusercontent.com/Bililive/BililiveRecorder/dev-1.3/configV2.schema.json"
|
||||
},
|
||||
"version": {
|
||||
"const": 2
|
||||
},
|
||||
"global": {
|
||||
"$ref": "#/definitions/global-config"
|
||||
},
|
||||
"rooms": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/room-config"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user