mirror of
https://github.com/BililiveRecorder/BililiveRecorder.git
synced 2024-11-16 03:32:20 +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": [{
|
"global": [{
|
||||||
"name": "TimingStreamRetry",
|
"name": "TimingStreamRetry",
|
||||||
"type": "uint",
|
"type": "uint",
|
||||||
|
@ -70,13 +70,14 @@ module.exports = {
|
||||||
"type": "int",
|
"type": "int",
|
||||||
"desc": "房间号",
|
"desc": "房间号",
|
||||||
"default": "default",
|
"default": "default",
|
||||||
"without_global": true
|
"without_global": true,
|
||||||
|
"web_readonly": true
|
||||||
}, {
|
}, {
|
||||||
"name": "AutoRecord",
|
"name": "AutoRecord",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"desc": "是否启用自动录制",
|
"desc": "是否启用自动录制",
|
||||||
"default": "default",
|
"default": "default",
|
||||||
"without_global": true
|
"without_global": true,
|
||||||
}, {
|
}, {
|
||||||
"name": "RecordMode",
|
"name": "RecordMode",
|
||||||
"type": "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)
|
switch (selection)
|
||||||
{
|
{
|
||||||
case JsonSchemaSelection.Default:
|
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;
|
break;
|
||||||
case JsonSchemaSelection.Custom:
|
case JsonSchemaSelection.Custom:
|
||||||
config.DollarSignSchema = AnsiConsole.Prompt(new TextPrompt<string>("[green]JSON Schema[/]:").AllowEmpty());
|
config.DollarSignSchema = AnsiConsole.Prompt(new TextPrompt<string>("[green]JSON Schema[/]:").AllowEmpty());
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace BililiveRecorder.Cli.Configure
|
||||||
{
|
{
|
||||||
public enum JsonSchemaSelection
|
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,
|
Default,
|
||||||
|
|
||||||
[Description("Custom")]
|
[Description("Custom")]
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
// ******************************
|
// ******************************
|
||||||
// GENERATED CODE, DO NOT EDIT.
|
// GENERATED CODE, DO NOT EDIT MANUALLY.
|
||||||
// RUN FORMATTER AFTER GENERATE
|
// SEE .tools/build_config.js
|
||||||
// ******************************
|
// ******************************
|
||||||
|
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using HierarchicalPropertyDefault;
|
using HierarchicalPropertyDefault;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
@ -337,7 +338,7 @@ namespace BililiveRecorder.Core.Config.V2
|
||||||
|
|
||||||
public sealed partial class DefaultConfig
|
public sealed partial class DefaultConfig
|
||||||
{
|
{
|
||||||
internal static readonly DefaultConfig Instance = new DefaultConfig();
|
public static readonly DefaultConfig Instance = new DefaultConfig();
|
||||||
private DefaultConfig() { }
|
private DefaultConfig() { }
|
||||||
|
|
||||||
public uint TimingStreamRetry => 6 * 1000;
|
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",
|
"$schema": "http://json-schema.org/schema",
|
||||||
"definitions": {
|
"definitions": {
|
||||||
"global-config": {
|
"global-config": {
|
||||||
|
@ -542,8 +542,7 @@
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"$schema": {
|
"$schema": {
|
||||||
"type": "string",
|
"const": "https://raw.githubusercontent.com/Bililive/BililiveRecorder/dev-1.3/configV2.schema.json"
|
||||||
"default": "https://raw.githubusercontent.com/Bililive/BililiveRecorder/dev-1.3/BililiveRecorder.Core/Config/V2/config.schema.json"
|
|
||||||
},
|
},
|
||||||
"version": {
|
"version": {
|
||||||
"const": 2
|
"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