mirror of
https://github.com/BililiveRecorder/BililiveRecorder.git
synced 2024-11-15 19:22:19 +08:00
Core: Create config v3
This commit is contained in:
parent
82ed148009
commit
8a65c9645d
|
@ -5,7 +5,7 @@
|
|||
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using BililiveRecorder.Core.Config.V2;
|
||||
using BililiveRecorder.Core.Config.V3;
|
||||
|
||||
namespace BililiveRecorder.Cli.Configure
|
||||
{
|
||||
|
@ -22,7 +22,7 @@ namespace BililiveRecorder.Cli.Configure
|
|||
RecordDanmakuGift,
|
||||
RecordDanmakuGuard,
|
||||
RecordingQuality,
|
||||
RecordFilenameFormat,
|
||||
FileNameRecordTemplate,
|
||||
WebHookUrls,
|
||||
WebHookUrlsV2,
|
||||
WpfShowTitleAndArea,
|
||||
|
@ -68,7 +68,7 @@ namespace BililiveRecorder.Cli.Configure
|
|||
GlobalConfig.Add(GlobalConfigProperties.RecordDanmakuGift, new ConfigInstruction<GlobalConfig, bool>(config => config.HasRecordDanmakuGift = false, (config, value) => config.RecordDanmakuGift = value) { Name = "RecordDanmakuGift", CanBeOptional = true });
|
||||
GlobalConfig.Add(GlobalConfigProperties.RecordDanmakuGuard, new ConfigInstruction<GlobalConfig, bool>(config => config.HasRecordDanmakuGuard = false, (config, value) => config.RecordDanmakuGuard = value) { Name = "RecordDanmakuGuard", CanBeOptional = true });
|
||||
GlobalConfig.Add(GlobalConfigProperties.RecordingQuality, new ConfigInstruction<GlobalConfig, string>(config => config.HasRecordingQuality = false, (config, value) => config.RecordingQuality = value) { Name = "RecordingQuality", CanBeOptional = true });
|
||||
GlobalConfig.Add(GlobalConfigProperties.RecordFilenameFormat, new ConfigInstruction<GlobalConfig, string>(config => config.HasRecordFilenameFormat = false, (config, value) => config.RecordFilenameFormat = value) { Name = "RecordFilenameFormat", CanBeOptional = true });
|
||||
GlobalConfig.Add(GlobalConfigProperties.FileNameRecordTemplate, new ConfigInstruction<GlobalConfig, string>(config => config.HasFileNameRecordTemplate = false, (config, value) => config.FileNameRecordTemplate = value) { Name = "FileNameRecordTemplate", CanBeOptional = true });
|
||||
GlobalConfig.Add(GlobalConfigProperties.WebHookUrls, new ConfigInstruction<GlobalConfig, string>(config => config.HasWebHookUrls = false, (config, value) => config.WebHookUrls = value) { Name = "WebHookUrls", CanBeOptional = true });
|
||||
GlobalConfig.Add(GlobalConfigProperties.WebHookUrlsV2, new ConfigInstruction<GlobalConfig, string>(config => config.HasWebHookUrlsV2 = false, (config, value) => config.WebHookUrlsV2 = value) { Name = "WebHookUrlsV2", CanBeOptional = true });
|
||||
GlobalConfig.Add(GlobalConfigProperties.WpfShowTitleAndArea, new ConfigInstruction<GlobalConfig, bool>(config => config.HasWpfShowTitleAndArea = false, (config, value) => config.WpfShowTitleAndArea = value) { Name = "WpfShowTitleAndArea", CanBeOptional = true });
|
||||
|
|
|
@ -4,9 +4,12 @@ using Newtonsoft.Json.Serialization;
|
|||
|
||||
namespace BililiveRecorder.Core.Config
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
[JsonConverter(typeof(JsonSubtypes), nameof(Version))]
|
||||
[JsonSubtypes.KnownSubType(typeof(V1.ConfigV1Wrapper), 1)]
|
||||
[JsonSubtypes.KnownSubType(typeof(V2.ConfigV2), 2)]
|
||||
[JsonSubtypes.KnownSubType(typeof(V3.ConfigV3), 3)]
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
public abstract class ConfigBase
|
||||
{
|
||||
[JsonProperty("$schema", Order = -2)]
|
||||
|
|
|
@ -3,40 +3,59 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using BililiveRecorder.Core.Config.V2;
|
||||
|
||||
#pragma warning disable CS0612 // obsolete
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
namespace BililiveRecorder.Core.Config
|
||||
{
|
||||
internal static class ConfigMapper
|
||||
{
|
||||
private static readonly Dictionary<PropertyInfo, PropertyInfo> Map1to2 = new();
|
||||
private static readonly Dictionary<PropertyInfo, PropertyInfo> Map2to3GlobalConfig = new();
|
||||
private static readonly Dictionary<PropertyInfo, PropertyInfo> Map2to3RoomConfig = new();
|
||||
|
||||
static ConfigMapper()
|
||||
{
|
||||
// Map v1 to v2
|
||||
{
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, uint>(Map1to2, x => x.TimingStreamRetry, x => x.TimingStreamRetry);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, uint>(Map1to2, x => x.TimingStreamConnect, x => x.TimingStreamConnect);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, uint>(Map1to2, x => x.TimingDanmakuRetry, x => x.TimingDanmakuRetry);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, uint>(Map1to2, x => x.TimingCheckInterval, x => x.TimingCheckInterval);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, uint>(Map1to2, x => x.TimingWatchdogTimeout, x => x.TimingWatchdogTimeout);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, uint>(Map1to2, x => x.RecordDanmakuFlushInterval, x => x.RecordDanmakuFlushInterval);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, string?>(Map1to2, x => x.Cookie, x => x.Cookie);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, string?>(Map1to2, x => x.WebHookUrls, x => x.WebHookUrls);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, string?>(Map1to2, x => x.LiveApiHost, x => x.LiveApiHost);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, string?>(Map1to2, x => x.RecordFilenameFormat, x => x.RecordFilenameFormat);
|
||||
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, CuttingMode>(Map1to2, x => x.CuttingMode, x => x.CuttingMode);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, uint>(Map1to2, x => x.CuttingNumber, x => x.CuttingNumber);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, bool>(Map1to2, x => x.RecordDanmaku, x => x.RecordDanmaku);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, bool>(Map1to2, x => x.RecordDanmakuRaw, x => x.RecordDanmakuRaw);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, bool>(Map1to2, x => x.RecordDanmakuSuperChat, x => x.RecordDanmakuSuperChat);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, bool>(Map1to2, x => x.RecordDanmakuGift, x => x.RecordDanmakuGift);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, bool>(Map1to2, x => x.RecordDanmakuGuard, x => x.RecordDanmakuGuard);
|
||||
}
|
||||
|
||||
// Map v2 to v3
|
||||
{
|
||||
Map2to3GlobalConfig = GetPropertyInfoPairs<V2.GlobalConfig, V3.GlobalConfig>()
|
||||
.Where(x => x.Key.Name.StartsWith("Optional", StringComparison.Ordinal))
|
||||
.ToDictionary(x => x.Key, x => x.Value);
|
||||
|
||||
Map2to3RoomConfig = GetPropertyInfoPairs<V2.RoomConfig, V3.RoomConfig>()
|
||||
.Where(x => x.Key.Name.StartsWith("Optional", StringComparison.Ordinal))
|
||||
.ToDictionary(x => x.Key, x => x.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public static V2.ConfigV2 Map1To2(V1.ConfigV1 v1)
|
||||
{
|
||||
var map = new Dictionary<PropertyInfo, PropertyInfo>();
|
||||
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, uint>(map, x => x.TimingStreamRetry, x => x.TimingStreamRetry);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, uint>(map, x => x.TimingStreamConnect, x => x.TimingStreamConnect);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, uint>(map, x => x.TimingDanmakuRetry, x => x.TimingDanmakuRetry);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, uint>(map, x => x.TimingCheckInterval, x => x.TimingCheckInterval);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, uint>(map, x => x.TimingWatchdogTimeout, x => x.TimingWatchdogTimeout);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, uint>(map, x => x.RecordDanmakuFlushInterval, x => x.RecordDanmakuFlushInterval);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, string?>(map, x => x.Cookie, x => x.Cookie);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, string?>(map, x => x.WebHookUrls, x => x.WebHookUrls);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, string?>(map, x => x.LiveApiHost, x => x.LiveApiHost);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, string?>(map, x => x.RecordFilenameFormat, x => x.RecordFilenameFormat);
|
||||
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, CuttingMode>(map, x => x.CuttingMode, x => x.CuttingMode);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, uint>(map, x => x.CuttingNumber, x => x.CuttingNumber);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, bool>(map, x => x.RecordDanmaku, x => x.RecordDanmaku);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, bool>(map, x => x.RecordDanmakuRaw, x => x.RecordDanmakuRaw);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, bool>(map, x => x.RecordDanmakuSuperChat, x => x.RecordDanmakuSuperChat);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, bool>(map, x => x.RecordDanmakuGift, x => x.RecordDanmakuGift);
|
||||
AddMap<V1.ConfigV1, V2.GlobalConfig, bool>(map, x => x.RecordDanmakuGuard, x => x.RecordDanmakuGuard);
|
||||
|
||||
var def = new V1.ConfigV1(); // old default
|
||||
var v2 = new V2.ConfigV2();
|
||||
|
||||
foreach (var item in map)
|
||||
foreach (var item in Map1to2)
|
||||
{
|
||||
var data = item.Key.GetValue(v1);
|
||||
if (!(data?.Equals(item.Key.GetValue(def)) ?? true))
|
||||
|
@ -48,6 +67,64 @@ namespace BililiveRecorder.Core.Config
|
|||
return v2;
|
||||
}
|
||||
|
||||
public static V3.ConfigV3 Map2To3(V2.ConfigV2 v2)
|
||||
{
|
||||
var v3 = new V3.ConfigV3();
|
||||
|
||||
// 复制没有变动的房间独立设置
|
||||
foreach (var v2room in v2.Rooms)
|
||||
{
|
||||
var v3room = new V3.RoomConfig();
|
||||
CopyValueWithMap(Map2to3RoomConfig, v2room, v3room);
|
||||
v3.Rooms.Add(v3room);
|
||||
}
|
||||
|
||||
// 复制没有变动的全局设置
|
||||
CopyValueWithMap(Map2to3GlobalConfig, v2.Global, v3.Global);
|
||||
|
||||
// 转换文件名格式
|
||||
// 如果用户设置了自定义的文件名格式才需要转换,否则使用全局默认
|
||||
if (v2.Global.HasRecordFilenameFormat && v2.Global.RecordFilenameFormat is not null)
|
||||
{
|
||||
v3.Global.FileNameRecordTemplate = v2.Global.RecordFilenameFormat.Replace("", ""); // TODO
|
||||
|
||||
}
|
||||
|
||||
throw new NotImplementedException("配置转换还未完成");
|
||||
|
||||
return v3;
|
||||
}
|
||||
|
||||
private static void CopyValueWithMap(Dictionary<PropertyInfo, PropertyInfo> map, object source, object target)
|
||||
{
|
||||
foreach (var item in map)
|
||||
{
|
||||
var data = item.Key.GetValue(source);
|
||||
item.Value.SetValue(target, data);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<KeyValuePair<PropertyInfo, PropertyInfo>> GetPropertyInfoPairs<T1, T2>()
|
||||
where T1 : class
|
||||
where T2 : class
|
||||
{
|
||||
var result = new List<KeyValuePair<PropertyInfo, PropertyInfo>>(32);
|
||||
|
||||
var t1Type = typeof(T1);
|
||||
var t2Type = typeof(T2);
|
||||
|
||||
var t1 = t1Type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
|
||||
|
||||
foreach (var p1 in t1)
|
||||
{
|
||||
var p2 = t2Type.GetProperty(p1.Name, BindingFlags.Instance | BindingFlags.Public);
|
||||
if (p2 != null)
|
||||
result.Add(new KeyValuePair<PropertyInfo, PropertyInfo>(p1, p2));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void AddMap<T1, T2, T3>(Dictionary<PropertyInfo, PropertyInfo> map, Expression<Func<T1, T3>> keyExpr, Expression<Func<T2, T3>> valueExpr)
|
||||
{
|
||||
var key = GetProperty(keyExpr);
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace BililiveRecorder.Core.Config
|
|||
NullValueHandling = NullValueHandling.Ignore
|
||||
};
|
||||
|
||||
public static V2.ConfigV2? LoadFrom(string directory)
|
||||
public static V3.ConfigV3? LoadFrom(string directory)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -30,7 +30,7 @@ namespace BililiveRecorder.Core.Config
|
|||
if (!File.Exists(filepath))
|
||||
{
|
||||
logger.Debug("Config file does not exist {Path}", filepath);
|
||||
return new V2.ConfigV2();
|
||||
return new V3.ConfigV3();
|
||||
}
|
||||
|
||||
logger.Debug("Loading config from {Path}", filepath);
|
||||
|
@ -45,7 +45,7 @@ namespace BililiveRecorder.Core.Config
|
|||
}
|
||||
}
|
||||
|
||||
public static V2.ConfigV2? LoadJson(string json)
|
||||
public static V3.ConfigV3? LoadJson(string json)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -61,14 +61,22 @@ namespace BililiveRecorder.Core.Config
|
|||
var v1Data = JsonConvert.DeserializeObject<V1.ConfigV1>(v1.Data ?? string.Empty);
|
||||
#pragma warning restore CS0612
|
||||
if (v1Data is null)
|
||||
return new V2.ConfigV2();
|
||||
return new V3.ConfigV3();
|
||||
|
||||
var newConfig = ConfigMapper.Map1To2(v1Data);
|
||||
var newConfig = ConfigMapper.Map2To3(ConfigMapper.Map1To2(v1Data));
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
case V2.ConfigV2 v2:
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
logger.Debug("读取到 config v2");
|
||||
return v2;
|
||||
return ConfigMapper.Map2To3(v2);
|
||||
|
||||
case V3.ConfigV3 v3:
|
||||
logger.Debug("读取到 config v3");
|
||||
return v3;
|
||||
|
||||
default:
|
||||
logger.Error("读取到不支持的设置版本");
|
||||
return null;
|
||||
|
@ -81,7 +89,7 @@ namespace BililiveRecorder.Core.Config
|
|||
}
|
||||
}
|
||||
|
||||
public static bool SaveTo(string directory, V2.ConfigV2 config)
|
||||
public static bool SaveTo(string directory, V3.ConfigV3 config)
|
||||
{
|
||||
if (config.DisableConfigSave)
|
||||
{
|
||||
|
@ -109,7 +117,7 @@ namespace BililiveRecorder.Core.Config
|
|||
}
|
||||
}
|
||||
|
||||
public static string? SaveJson(V2.ConfigV2 config)
|
||||
public static string? SaveJson(V3.ConfigV3 config)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
namespace BililiveRecorder.Core.Config.V2
|
||||
namespace BililiveRecorder.Core.Config
|
||||
{
|
||||
public enum CuttingMode : int
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace BililiveRecorder.Core.Config.V2
|
||||
namespace BililiveRecorder.Core.Config
|
||||
{
|
||||
public enum RecordMode : int
|
||||
{
|
|
@ -41,7 +41,7 @@ namespace BililiveRecorder.Core.Config.V1
|
|||
/// 自动切割模式
|
||||
/// </summary>
|
||||
[JsonProperty("cutting_mode")]
|
||||
public V2.CuttingMode CuttingMode { get => this._cuttingMode; set => this.SetField(ref this._cuttingMode, value); }
|
||||
public CuttingMode CuttingMode { get => this._cuttingMode; set => this.SetField(ref this._cuttingMode, value); }
|
||||
|
||||
/// <summary>
|
||||
/// 自动切割数值(分钟/MiB)
|
||||
|
@ -166,7 +166,7 @@ namespace BililiveRecorder.Core.Config.V1
|
|||
private uint _clipLengthFuture = 10;
|
||||
private uint _cuttingNumber = 10;
|
||||
//private EnabledFeature _enabledFeature = EnabledFeature.RecordOnly;
|
||||
private V2.CuttingMode _cuttingMode = V2.CuttingMode.Disabled;
|
||||
private CuttingMode _cuttingMode = CuttingMode.Disabled;
|
||||
|
||||
private uint _timingWatchdogTimeout = 10 * 1000;
|
||||
private uint _timingStreamRetry = 6 * 1000;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
#nullable enable
|
||||
namespace BililiveRecorder.Core.Config.V2
|
||||
{
|
||||
[Obsolete("Use Config v3")]
|
||||
public class ConfigV2 : ConfigBase
|
||||
{
|
||||
public override int Version => 2;
|
||||
|
@ -18,6 +20,7 @@ namespace BililiveRecorder.Core.Config.V2
|
|||
public bool DisableConfigSave { get; set; } = false; // for CLI
|
||||
}
|
||||
|
||||
[Obsolete("Use Config v3")]
|
||||
public partial class RoomConfig
|
||||
{
|
||||
public RoomConfig() : base(x => x.AutoMap(p => new[] { "Has" + p.Name }))
|
||||
|
@ -28,6 +31,7 @@ namespace BililiveRecorder.Core.Config.V2
|
|||
public string? WorkDirectory => this.GetPropertyValue<string>();
|
||||
}
|
||||
|
||||
[Obsolete("Use Config v3")]
|
||||
public partial class GlobalConfig
|
||||
{
|
||||
public GlobalConfig() : base(x => x.AutoMap(p => new[] { "Has" + p.Name }))
|
||||
|
|
403
BililiveRecorder.Core/Config/V3/Config.gen.cs
Normal file
403
BililiveRecorder.Core/Config/V3/Config.gen.cs
Normal file
|
@ -0,0 +1,403 @@
|
|||
// ******************************
|
||||
// GENERATED CODE, DO NOT EDIT MANUALLY.
|
||||
// SEE /config_gen/README.md
|
||||
// ******************************
|
||||
|
||||
using System.ComponentModel;
|
||||
using HierarchicalPropertyDefault;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
#nullable enable
|
||||
namespace BililiveRecorder.Core.Config.V3
|
||||
{
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
public sealed partial class RoomConfig : HierarchicalObject<GlobalConfig, RoomConfig>
|
||||
{
|
||||
/// <summary>
|
||||
/// 房间号
|
||||
/// </summary>
|
||||
public int RoomId { get => this.GetPropertyValue<int>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasRoomId { get => this.GetPropertyHasValue(nameof(this.RoomId)); set => this.SetPropertyHasValue<int>(value, nameof(this.RoomId)); }
|
||||
[JsonProperty(nameof(RoomId)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<int> OptionalRoomId { get => this.GetPropertyValueOptional<int>(nameof(this.RoomId)); set => this.SetPropertyValueOptional(value, nameof(this.RoomId)); }
|
||||
|
||||
/// <summary>
|
||||
/// 自动录制
|
||||
/// </summary>
|
||||
public bool AutoRecord { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasAutoRecord { get => this.GetPropertyHasValue(nameof(this.AutoRecord)); set => this.SetPropertyHasValue<bool>(value, nameof(this.AutoRecord)); }
|
||||
[JsonProperty(nameof(AutoRecord)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<bool> OptionalAutoRecord { get => this.GetPropertyValueOptional<bool>(nameof(this.AutoRecord)); set => this.SetPropertyValueOptional(value, nameof(this.AutoRecord)); }
|
||||
|
||||
/// <summary>
|
||||
/// 录制模式
|
||||
/// </summary>
|
||||
public RecordMode RecordMode { get => this.GetPropertyValue<RecordMode>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasRecordMode { get => this.GetPropertyHasValue(nameof(this.RecordMode)); set => this.SetPropertyHasValue<RecordMode>(value, nameof(this.RecordMode)); }
|
||||
[JsonProperty(nameof(RecordMode)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<RecordMode> OptionalRecordMode { get => this.GetPropertyValueOptional<RecordMode>(nameof(this.RecordMode)); set => this.SetPropertyValueOptional(value, nameof(this.RecordMode)); }
|
||||
|
||||
/// <summary>
|
||||
/// 自动分段模式
|
||||
/// </summary>
|
||||
public CuttingMode CuttingMode { get => this.GetPropertyValue<CuttingMode>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasCuttingMode { get => this.GetPropertyHasValue(nameof(this.CuttingMode)); set => this.SetPropertyHasValue<CuttingMode>(value, nameof(this.CuttingMode)); }
|
||||
[JsonProperty(nameof(CuttingMode)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<CuttingMode> OptionalCuttingMode { get => this.GetPropertyValueOptional<CuttingMode>(nameof(this.CuttingMode)); set => this.SetPropertyValueOptional(value, nameof(this.CuttingMode)); }
|
||||
|
||||
/// <summary>
|
||||
/// 自动分段数值
|
||||
/// </summary>
|
||||
public uint CuttingNumber { get => this.GetPropertyValue<uint>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasCuttingNumber { get => this.GetPropertyHasValue(nameof(this.CuttingNumber)); set => this.SetPropertyHasValue<uint>(value, nameof(this.CuttingNumber)); }
|
||||
[JsonProperty(nameof(CuttingNumber)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<uint> OptionalCuttingNumber { get => this.GetPropertyValueOptional<uint>(nameof(this.CuttingNumber)); set => this.SetPropertyValueOptional(value, nameof(this.CuttingNumber)); }
|
||||
|
||||
/// <summary>
|
||||
/// 弹幕录制
|
||||
/// </summary>
|
||||
public bool RecordDanmaku { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasRecordDanmaku { get => this.GetPropertyHasValue(nameof(this.RecordDanmaku)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmaku)); }
|
||||
[JsonProperty(nameof(RecordDanmaku)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<bool> OptionalRecordDanmaku { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmaku)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmaku)); }
|
||||
|
||||
/// <summary>
|
||||
/// 弹幕录制-原始数据
|
||||
/// </summary>
|
||||
public bool RecordDanmakuRaw { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasRecordDanmakuRaw { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuRaw)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmakuRaw)); }
|
||||
[JsonProperty(nameof(RecordDanmakuRaw)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<bool> OptionalRecordDanmakuRaw { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmakuRaw)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuRaw)); }
|
||||
|
||||
/// <summary>
|
||||
/// 弹幕录制-SuperChat
|
||||
/// </summary>
|
||||
public bool RecordDanmakuSuperChat { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasRecordDanmakuSuperChat { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuSuperChat)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmakuSuperChat)); }
|
||||
[JsonProperty(nameof(RecordDanmakuSuperChat)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<bool> OptionalRecordDanmakuSuperChat { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmakuSuperChat)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuSuperChat)); }
|
||||
|
||||
/// <summary>
|
||||
/// 弹幕录制-礼物
|
||||
/// </summary>
|
||||
public bool RecordDanmakuGift { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasRecordDanmakuGift { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuGift)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmakuGift)); }
|
||||
[JsonProperty(nameof(RecordDanmakuGift)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<bool> OptionalRecordDanmakuGift { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmakuGift)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuGift)); }
|
||||
|
||||
/// <summary>
|
||||
/// 弹幕录制-上船
|
||||
/// </summary>
|
||||
public bool RecordDanmakuGuard { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasRecordDanmakuGuard { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuGuard)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmakuGuard)); }
|
||||
[JsonProperty(nameof(RecordDanmakuGuard)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<bool> OptionalRecordDanmakuGuard { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmakuGuard)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuGuard)); }
|
||||
|
||||
/// <summary>
|
||||
/// 直播画质
|
||||
/// </summary>
|
||||
public string? RecordingQuality { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasRecordingQuality { get => this.GetPropertyHasValue(nameof(this.RecordingQuality)); set => this.SetPropertyHasValue<string>(value, nameof(this.RecordingQuality)); }
|
||||
[JsonProperty(nameof(RecordingQuality)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<string?> OptionalRecordingQuality { get => this.GetPropertyValueOptional<string>(nameof(this.RecordingQuality)); set => this.SetPropertyValueOptional(value, nameof(this.RecordingQuality)); }
|
||||
|
||||
/// <summary>
|
||||
/// 录制文件名模板
|
||||
/// </summary>
|
||||
public string? FileNameRecordTemplate => this.GetPropertyValue<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 录制文件写入结束 Webhook 地址 每行一个
|
||||
/// </summary>
|
||||
public string? WebHookUrls => this.GetPropertyValue<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Webhook v2 地址 每行一个
|
||||
/// </summary>
|
||||
public string? WebHookUrlsV2 => this.GetPropertyValue<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 在界面显示标题和分区
|
||||
/// </summary>
|
||||
public bool WpfShowTitleAndArea => this.GetPropertyValue<bool>();
|
||||
|
||||
/// <summary>
|
||||
/// 请求 API 时使用的 Cookie
|
||||
/// </summary>
|
||||
public string? Cookie => this.GetPropertyValue<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 替换 api.live.bilibili.com 服务器为其他反代,可以支持在云服务器上录制
|
||||
/// </summary>
|
||||
public string? LiveApiHost => this.GetPropertyValue<string>();
|
||||
|
||||
/// <summary>
|
||||
/// HTTP API 检查时间间隔 秒
|
||||
/// </summary>
|
||||
public uint TimingCheckInterval => this.GetPropertyValue<uint>();
|
||||
|
||||
/// <summary>
|
||||
/// 录制断开重连时间间隔 毫秒
|
||||
/// </summary>
|
||||
public uint TimingStreamRetry => this.GetPropertyValue<uint>();
|
||||
|
||||
/// <summary>
|
||||
/// 录制无指定画质重连时间间隔 秒
|
||||
/// </summary>
|
||||
public uint TimingStreamRetryNoQn => this.GetPropertyValue<uint>();
|
||||
|
||||
/// <summary>
|
||||
/// 连接直播服务器超时时间 毫秒
|
||||
/// </summary>
|
||||
public uint TimingStreamConnect => this.GetPropertyValue<uint>();
|
||||
|
||||
/// <summary>
|
||||
/// 弹幕服务器重连时间间隔 毫秒
|
||||
/// </summary>
|
||||
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>();
|
||||
|
||||
}
|
||||
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
public sealed partial class GlobalConfig : HierarchicalObject<DefaultConfig, GlobalConfig>
|
||||
{
|
||||
/// <summary>
|
||||
/// 录制模式
|
||||
/// </summary>
|
||||
public RecordMode RecordMode { get => this.GetPropertyValue<RecordMode>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasRecordMode { get => this.GetPropertyHasValue(nameof(this.RecordMode)); set => this.SetPropertyHasValue<RecordMode>(value, nameof(this.RecordMode)); }
|
||||
[JsonProperty(nameof(RecordMode)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<RecordMode> OptionalRecordMode { get => this.GetPropertyValueOptional<RecordMode>(nameof(this.RecordMode)); set => this.SetPropertyValueOptional(value, nameof(this.RecordMode)); }
|
||||
|
||||
/// <summary>
|
||||
/// 自动分段模式
|
||||
/// </summary>
|
||||
public CuttingMode CuttingMode { get => this.GetPropertyValue<CuttingMode>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasCuttingMode { get => this.GetPropertyHasValue(nameof(this.CuttingMode)); set => this.SetPropertyHasValue<CuttingMode>(value, nameof(this.CuttingMode)); }
|
||||
[JsonProperty(nameof(CuttingMode)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<CuttingMode> OptionalCuttingMode { get => this.GetPropertyValueOptional<CuttingMode>(nameof(this.CuttingMode)); set => this.SetPropertyValueOptional(value, nameof(this.CuttingMode)); }
|
||||
|
||||
/// <summary>
|
||||
/// 自动分段数值
|
||||
/// </summary>
|
||||
public uint CuttingNumber { get => this.GetPropertyValue<uint>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasCuttingNumber { get => this.GetPropertyHasValue(nameof(this.CuttingNumber)); set => this.SetPropertyHasValue<uint>(value, nameof(this.CuttingNumber)); }
|
||||
[JsonProperty(nameof(CuttingNumber)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<uint> OptionalCuttingNumber { get => this.GetPropertyValueOptional<uint>(nameof(this.CuttingNumber)); set => this.SetPropertyValueOptional(value, nameof(this.CuttingNumber)); }
|
||||
|
||||
/// <summary>
|
||||
/// 弹幕录制
|
||||
/// </summary>
|
||||
public bool RecordDanmaku { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasRecordDanmaku { get => this.GetPropertyHasValue(nameof(this.RecordDanmaku)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmaku)); }
|
||||
[JsonProperty(nameof(RecordDanmaku)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<bool> OptionalRecordDanmaku { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmaku)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmaku)); }
|
||||
|
||||
/// <summary>
|
||||
/// 弹幕录制-原始数据
|
||||
/// </summary>
|
||||
public bool RecordDanmakuRaw { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasRecordDanmakuRaw { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuRaw)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmakuRaw)); }
|
||||
[JsonProperty(nameof(RecordDanmakuRaw)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<bool> OptionalRecordDanmakuRaw { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmakuRaw)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuRaw)); }
|
||||
|
||||
/// <summary>
|
||||
/// 弹幕录制-SuperChat
|
||||
/// </summary>
|
||||
public bool RecordDanmakuSuperChat { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasRecordDanmakuSuperChat { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuSuperChat)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmakuSuperChat)); }
|
||||
[JsonProperty(nameof(RecordDanmakuSuperChat)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<bool> OptionalRecordDanmakuSuperChat { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmakuSuperChat)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuSuperChat)); }
|
||||
|
||||
/// <summary>
|
||||
/// 弹幕录制-礼物
|
||||
/// </summary>
|
||||
public bool RecordDanmakuGift { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasRecordDanmakuGift { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuGift)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmakuGift)); }
|
||||
[JsonProperty(nameof(RecordDanmakuGift)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<bool> OptionalRecordDanmakuGift { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmakuGift)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuGift)); }
|
||||
|
||||
/// <summary>
|
||||
/// 弹幕录制-上船
|
||||
/// </summary>
|
||||
public bool RecordDanmakuGuard { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasRecordDanmakuGuard { get => this.GetPropertyHasValue(nameof(this.RecordDanmakuGuard)); set => this.SetPropertyHasValue<bool>(value, nameof(this.RecordDanmakuGuard)); }
|
||||
[JsonProperty(nameof(RecordDanmakuGuard)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<bool> OptionalRecordDanmakuGuard { get => this.GetPropertyValueOptional<bool>(nameof(this.RecordDanmakuGuard)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuGuard)); }
|
||||
|
||||
/// <summary>
|
||||
/// 直播画质
|
||||
/// </summary>
|
||||
public string? RecordingQuality { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasRecordingQuality { get => this.GetPropertyHasValue(nameof(this.RecordingQuality)); set => this.SetPropertyHasValue<string>(value, nameof(this.RecordingQuality)); }
|
||||
[JsonProperty(nameof(RecordingQuality)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<string?> OptionalRecordingQuality { get => this.GetPropertyValueOptional<string>(nameof(this.RecordingQuality)); set => this.SetPropertyValueOptional(value, nameof(this.RecordingQuality)); }
|
||||
|
||||
/// <summary>
|
||||
/// 录制文件名模板
|
||||
/// </summary>
|
||||
public string? FileNameRecordTemplate { get => this.GetPropertyValue<string>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasFileNameRecordTemplate { get => this.GetPropertyHasValue(nameof(this.FileNameRecordTemplate)); set => this.SetPropertyHasValue<string>(value, nameof(this.FileNameRecordTemplate)); }
|
||||
[JsonProperty(nameof(FileNameRecordTemplate)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<string?> OptionalFileNameRecordTemplate { get => this.GetPropertyValueOptional<string>(nameof(this.FileNameRecordTemplate)); set => this.SetPropertyValueOptional(value, nameof(this.FileNameRecordTemplate)); }
|
||||
|
||||
/// <summary>
|
||||
/// 录制文件写入结束 Webhook 地址 每行一个
|
||||
/// </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)); }
|
||||
[JsonProperty(nameof(WebHookUrls)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<string?> OptionalWebHookUrls { get => this.GetPropertyValueOptional<string>(nameof(this.WebHookUrls)); set => this.SetPropertyValueOptional(value, nameof(this.WebHookUrls)); }
|
||||
|
||||
/// <summary>
|
||||
/// Webhook v2 地址 每行一个
|
||||
/// </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)); }
|
||||
[JsonProperty(nameof(WebHookUrlsV2)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<string?> OptionalWebHookUrlsV2 { get => this.GetPropertyValueOptional<string>(nameof(this.WebHookUrlsV2)); set => this.SetPropertyValueOptional(value, nameof(this.WebHookUrlsV2)); }
|
||||
|
||||
/// <summary>
|
||||
/// 在界面显示标题和分区
|
||||
/// </summary>
|
||||
public bool WpfShowTitleAndArea { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasWpfShowTitleAndArea { get => this.GetPropertyHasValue(nameof(this.WpfShowTitleAndArea)); set => this.SetPropertyHasValue<bool>(value, nameof(this.WpfShowTitleAndArea)); }
|
||||
[JsonProperty(nameof(WpfShowTitleAndArea)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<bool> OptionalWpfShowTitleAndArea { get => this.GetPropertyValueOptional<bool>(nameof(this.WpfShowTitleAndArea)); set => this.SetPropertyValueOptional(value, nameof(this.WpfShowTitleAndArea)); }
|
||||
|
||||
/// <summary>
|
||||
/// 请求 API 时使用的 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)); }
|
||||
[JsonProperty(nameof(Cookie)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<string?> OptionalCookie { get => this.GetPropertyValueOptional<string>(nameof(this.Cookie)); set => this.SetPropertyValueOptional(value, nameof(this.Cookie)); }
|
||||
|
||||
/// <summary>
|
||||
/// 替换 api.live.bilibili.com 服务器为其他反代,可以支持在云服务器上录制
|
||||
/// </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)); }
|
||||
[JsonProperty(nameof(LiveApiHost)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
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)); }
|
||||
[JsonProperty(nameof(TimingCheckInterval)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<uint> OptionalTimingCheckInterval { get => this.GetPropertyValueOptional<uint>(nameof(this.TimingCheckInterval)); set => this.SetPropertyValueOptional(value, nameof(this.TimingCheckInterval)); }
|
||||
|
||||
/// <summary>
|
||||
/// 录制断开重连时间间隔 毫秒
|
||||
/// </summary>
|
||||
public uint TimingStreamRetry { get => this.GetPropertyValue<uint>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasTimingStreamRetry { get => this.GetPropertyHasValue(nameof(this.TimingStreamRetry)); set => this.SetPropertyHasValue<uint>(value, nameof(this.TimingStreamRetry)); }
|
||||
[JsonProperty(nameof(TimingStreamRetry)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<uint> OptionalTimingStreamRetry { get => this.GetPropertyValueOptional<uint>(nameof(this.TimingStreamRetry)); set => this.SetPropertyValueOptional(value, nameof(this.TimingStreamRetry)); }
|
||||
|
||||
/// <summary>
|
||||
/// 录制无指定画质重连时间间隔 秒
|
||||
/// </summary>
|
||||
public uint TimingStreamRetryNoQn { get => this.GetPropertyValue<uint>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasTimingStreamRetryNoQn { get => this.GetPropertyHasValue(nameof(this.TimingStreamRetryNoQn)); set => this.SetPropertyHasValue<uint>(value, nameof(this.TimingStreamRetryNoQn)); }
|
||||
[JsonProperty(nameof(TimingStreamRetryNoQn)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<uint> OptionalTimingStreamRetryNoQn { get => this.GetPropertyValueOptional<uint>(nameof(this.TimingStreamRetryNoQn)); set => this.SetPropertyValueOptional(value, nameof(this.TimingStreamRetryNoQn)); }
|
||||
|
||||
/// <summary>
|
||||
/// 连接直播服务器超时时间 毫秒
|
||||
/// </summary>
|
||||
public uint TimingStreamConnect { get => this.GetPropertyValue<uint>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasTimingStreamConnect { get => this.GetPropertyHasValue(nameof(this.TimingStreamConnect)); set => this.SetPropertyHasValue<uint>(value, nameof(this.TimingStreamConnect)); }
|
||||
[JsonProperty(nameof(TimingStreamConnect)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<uint> OptionalTimingStreamConnect { get => this.GetPropertyValueOptional<uint>(nameof(this.TimingStreamConnect)); set => this.SetPropertyValueOptional(value, nameof(this.TimingStreamConnect)); }
|
||||
|
||||
/// <summary>
|
||||
/// 弹幕服务器重连时间间隔 毫秒
|
||||
/// </summary>
|
||||
public uint TimingDanmakuRetry { get => this.GetPropertyValue<uint>(); set => this.SetPropertyValue(value); }
|
||||
public bool HasTimingDanmakuRetry { get => this.GetPropertyHasValue(nameof(this.TimingDanmakuRetry)); set => this.SetPropertyHasValue<uint>(value, nameof(this.TimingDanmakuRetry)); }
|
||||
[JsonProperty(nameof(TimingDanmakuRetry)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
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)); }
|
||||
[JsonProperty(nameof(TimingWatchdogTimeout)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
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)); }
|
||||
[JsonProperty(nameof(RecordDanmakuFlushInterval)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Optional<uint> OptionalRecordDanmakuFlushInterval { get => this.GetPropertyValueOptional<uint>(nameof(this.RecordDanmakuFlushInterval)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuFlushInterval)); }
|
||||
|
||||
}
|
||||
|
||||
public sealed partial class DefaultConfig
|
||||
{
|
||||
public static readonly DefaultConfig Instance = new DefaultConfig();
|
||||
private DefaultConfig() { }
|
||||
|
||||
public RecordMode RecordMode => RecordMode.Standard;
|
||||
|
||||
public CuttingMode CuttingMode => CuttingMode.Disabled;
|
||||
|
||||
public uint CuttingNumber => 100;
|
||||
|
||||
public bool RecordDanmaku => false;
|
||||
|
||||
public bool RecordDanmakuRaw => false;
|
||||
|
||||
public bool RecordDanmakuSuperChat => true;
|
||||
|
||||
public bool RecordDanmakuGift => false;
|
||||
|
||||
public bool RecordDanmakuGuard => true;
|
||||
|
||||
public string RecordingQuality => "10000";
|
||||
|
||||
public string FileNameRecordTemplate => @"{roomid}-{name}/录制-{roomid}-{date}-{time}-{ms}-{title}.flv";
|
||||
|
||||
public string WebHookUrls => string.Empty;
|
||||
|
||||
public string WebHookUrlsV2 => string.Empty;
|
||||
|
||||
public bool WpfShowTitleAndArea => true;
|
||||
|
||||
public string Cookie => string.Empty;
|
||||
|
||||
public string LiveApiHost => "https://api.live.bilibili.com";
|
||||
|
||||
public uint TimingCheckInterval => 10 * 60;
|
||||
|
||||
public uint TimingStreamRetry => 6 * 1000;
|
||||
|
||||
public uint TimingStreamRetryNoQn => 90;
|
||||
|
||||
public uint TimingStreamConnect => 5 * 1000;
|
||||
|
||||
public uint TimingDanmakuRetry => 9 * 1000;
|
||||
|
||||
public uint TimingWatchdogTimeout => 10 * 1000;
|
||||
|
||||
public uint RecordDanmakuFlushInterval => 20;
|
||||
|
||||
}
|
||||
|
||||
}
|
47
BililiveRecorder.Core/Config/V3/ConfigV3.cs
Normal file
47
BililiveRecorder.Core/Config/V3/ConfigV3.cs
Normal file
|
@ -0,0 +1,47 @@
|
|||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
#nullable enable
|
||||
namespace BililiveRecorder.Core.Config.V3
|
||||
{
|
||||
public class ConfigV3 : ConfigBase
|
||||
{
|
||||
public override int Version => 3;
|
||||
|
||||
[JsonProperty("global")]
|
||||
public GlobalConfig Global { get; set; } = new GlobalConfig();
|
||||
|
||||
[JsonProperty("rooms")]
|
||||
public List<RoomConfig> Rooms { get; set; } = new List<RoomConfig>();
|
||||
|
||||
[JsonIgnore]
|
||||
public bool DisableConfigSave { get; set; } = false; // for CLI
|
||||
}
|
||||
|
||||
public partial class RoomConfig
|
||||
{
|
||||
public RoomConfig() : base(x => x.AutoMap(p => new[] { "Has" + p.Name }))
|
||||
{ }
|
||||
|
||||
internal void SetParent(GlobalConfig? config) => this.Parent = config;
|
||||
|
||||
public string? WorkDirectory => this.GetPropertyValue<string>();
|
||||
}
|
||||
|
||||
public partial class GlobalConfig
|
||||
{
|
||||
public GlobalConfig() : base(x => x.AutoMap(p => new[] { "Has" + p.Name }))
|
||||
{
|
||||
this.Parent = DefaultConfig.Instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前工作目录
|
||||
/// </summary>
|
||||
public string? WorkDirectory
|
||||
{
|
||||
get => this.GetPropertyValue<string>();
|
||||
set => this.SetPropertyValue(value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
using System;
|
||||
using BililiveRecorder.Core.Config.V2;
|
||||
using BililiveRecorder.Core.Config;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Serilog;
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ using System.Linq;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BililiveRecorder.Core.Api;
|
||||
using BililiveRecorder.Core.Config;
|
||||
using BililiveRecorder.Core.Event;
|
||||
using BililiveRecorder.Core.ProcessingRules;
|
||||
using BililiveRecorder.Flv;
|
||||
|
@ -239,11 +240,11 @@ namespace BililiveRecorder.Core.Recording
|
|||
{
|
||||
switch (this.room.RoomConfig.CuttingMode)
|
||||
{
|
||||
case Config.V2.CuttingMode.ByTime:
|
||||
case CuttingMode.ByTime:
|
||||
if (e.FileMaxTimestamp > this.room.RoomConfig.CuttingNumber * (60u * 1000u))
|
||||
this.splitFileRule.SetSplitBeforeFlag();
|
||||
break;
|
||||
case Config.V2.CuttingMode.BySize:
|
||||
case CuttingMode.BySize:
|
||||
if ((e.CurrentFileSize + (e.OutputVideoByteCount * 1.1) + e.OutputAudioByteCount) / (1024d * 1024d) > this.room.RoomConfig.CuttingNumber)
|
||||
this.splitFileRule.SetSplitBeforeFlag();
|
||||
break;
|
||||
|
|
611
configV3.schema.json
Normal file
611
configV3.schema.json
Normal file
|
@ -0,0 +1,611 @@
|
|||
{
|
||||
"$comment": "GENERATED CODE, DO NOT EDIT MANUALLY.",
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"definitions": {
|
||||
"global-config": {
|
||||
"description": "全局设置",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"FileNameRecordTemplate": {
|
||||
"description": "录制文件名模板\n默认: {roomid}-{name}/录制-{roomid}-{date}-{time}-{ms}-{title}.flv",
|
||||
"markdownDescription": "录制文件名模板 \n默认: `{roomid}-{name}/录制-{roomid}-{date}-{time}-{ms}-{title}.flv `\n\nTODO: config v3 新的文件名模板系统的文档还没有写",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "string",
|
||||
"default": "{roomid}-{name}/录制-{roomid}-{date}-{time}-{ms}-{title}.flv"
|
||||
}
|
||||
}
|
||||
},
|
||||
"WebHookUrls": {
|
||||
"description": "WebhookV1\n默认: ",
|
||||
"markdownDescription": "WebhookV1 \n默认: ` `\n\n具体文档见 [Webhook](/docs/basic/webhook/)",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"WebHookUrlsV2": {
|
||||
"description": "WebhookV2\n默认: ",
|
||||
"markdownDescription": "WebhookV2 \n默认: ` `\n\n具体文档见 [Webhook](/docs/basic/webhook/)",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"WpfShowTitleAndArea": {
|
||||
"description": "在界面显示标题和分区\n默认: true",
|
||||
"markdownDescription": "在界面显示标题和分区 \n默认: `true `\n\n只在桌面版(WPF版)有效",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"Cookie": {
|
||||
"description": "请求 API 时使用的 Cookie\n默认: (空字符串)",
|
||||
"markdownDescription": "请求 API 时使用的 Cookie \n默认: `(空字符串) `\n\n",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "string",
|
||||
"pattern": "^(S+=S+;? ?)*$",
|
||||
"maxLength": 4096
|
||||
}
|
||||
}
|
||||
},
|
||||
"LiveApiHost": {
|
||||
"description": "请求的 API Host\n默认: https://api.live.bilibili.com",
|
||||
"markdownDescription": "请求的 API Host \n默认: `https://api.live.bilibili.com `\n\n",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "string",
|
||||
"default": "https://api.live.bilibili.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TimingCheckInterval": {
|
||||
"description": "HTTP API 检查时间间隔 秒\n默认: 600 (10分)",
|
||||
"markdownDescription": "HTTP API 检查时间间隔 秒 \n默认: `600 (10分) `\n\n",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4294967295,
|
||||
"default": 600
|
||||
}
|
||||
}
|
||||
},
|
||||
"TimingStreamRetry": {
|
||||
"description": "录制断开重连时间间隔 毫秒\n默认: 6000 (6秒)",
|
||||
"markdownDescription": "录制断开重连时间间隔 毫秒 \n默认: `6000 (6秒) `\n\n",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4294967295,
|
||||
"default": 6000
|
||||
}
|
||||
}
|
||||
},
|
||||
"TimingStreamRetryNoQn": {
|
||||
"description": "录制无指定画质重连时间间隔 秒\n默认: 90 (1.5分钟)",
|
||||
"markdownDescription": "录制无指定画质重连时间间隔 秒 \n默认: `90 (1.5分钟) `\n\n",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4294967295,
|
||||
"default": 90
|
||||
}
|
||||
}
|
||||
},
|
||||
"TimingStreamConnect": {
|
||||
"description": "连接直播服务器超时时间 毫秒\n默认: 5000 (5秒)",
|
||||
"markdownDescription": "连接直播服务器超时时间 毫秒 \n默认: `5000 (5秒) `\n\n",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4294967295,
|
||||
"default": 5000
|
||||
}
|
||||
}
|
||||
},
|
||||
"TimingDanmakuRetry": {
|
||||
"description": "弹幕服务器重连时间间隔 毫秒\n默认: 9000 (9秒)",
|
||||
"markdownDescription": "弹幕服务器重连时间间隔 毫秒 \n默认: `9000 (9秒) `\n\n",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4294967295,
|
||||
"default": 9000
|
||||
}
|
||||
}
|
||||
},
|
||||
"TimingWatchdogTimeout": {
|
||||
"description": "最大允许未收到直播数据时间 毫秒\n默认: 10000 (10秒)",
|
||||
"markdownDescription": "最大允许未收到直播数据时间 毫秒 \n默认: `10000 (10秒) `\n\n",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4294967295,
|
||||
"default": 10000
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmakuFlushInterval": {
|
||||
"description": "触发刷新弹幕写入缓冲的个数\n默认: 20",
|
||||
"markdownDescription": "触发刷新弹幕写入缓冲的个数 \n默认: `20 `\n\n",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4294967295,
|
||||
"default": 20
|
||||
}
|
||||
}
|
||||
},
|
||||
"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/)",
|
||||
"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": "自动分段模式\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 |",
|
||||
"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": "自动分段数值\n默认: 100",
|
||||
"markdownDescription": "自动分段数值 \n默认: `100 `\n\n根据 CuttingMode 设置的不同: \n当按时长分段时,本设置的单位为分钟。 \n当按大小分段时,本设置的单位为MiB。",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4294967295,
|
||||
"default": 100
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmaku": {
|
||||
"description": "弹幕录制\n默认: false",
|
||||
"markdownDescription": "弹幕录制 \n默认: `false `\n\n是否录制弹幕,`true` 为录制,`false` 为不录制。\n\n本设置同时是所有“弹幕录制”的总开关,当本设置为 `false` 时其他所有“弹幕录制”设置无效,不会写入弹幕XML文件。",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmakuRaw": {
|
||||
"description": "弹幕录制-原始数据\n默认: false",
|
||||
"markdownDescription": "弹幕录制-原始数据 \n默认: `false `\n\n是否记录原始 JSON 数据。\n\n弹幕原始数据会保存到 XML 文件每一条弹幕数据的 `raw` attribute 上。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmakuSuperChat": {
|
||||
"description": "弹幕录制-SuperChat\n默认: true",
|
||||
"markdownDescription": "弹幕录制-SuperChat \n默认: `true `\n\n是否记录 SuperChat。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmakuGift": {
|
||||
"description": "弹幕录制-礼物\n默认: false",
|
||||
"markdownDescription": "弹幕录制-礼物 \n默认: `false `\n\n是否记录礼物。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmakuGuard": {
|
||||
"description": "弹幕录制-上船\n默认: true",
|
||||
"markdownDescription": "弹幕录制-上船 \n默认: `true `\n\n是否记录上船(购买舰长)。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"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",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "string",
|
||||
"default": "10000"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"room-config": {
|
||||
"description": "房间独立设置",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"RoomId": {
|
||||
"description": "房间号\n默认: default",
|
||||
"markdownDescription": "房间号 \n默认: `default `\n\n",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"minimum": -2147483648,
|
||||
"maximum": 2147483647
|
||||
}
|
||||
}
|
||||
},
|
||||
"AutoRecord": {
|
||||
"description": "自动录制\n默认: default",
|
||||
"markdownDescription": "自动录制 \n默认: `default `\n\n设为 `true` 为启用自动录制,`false` 为不自动录制。",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"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/)",
|
||||
"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": "自动分段模式\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 |",
|
||||
"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": "自动分段数值\n默认: 100",
|
||||
"markdownDescription": "自动分段数值 \n默认: `100 `\n\n根据 CuttingMode 设置的不同: \n当按时长分段时,本设置的单位为分钟。 \n当按大小分段时,本设置的单位为MiB。",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4294967295,
|
||||
"default": 100
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmaku": {
|
||||
"description": "弹幕录制\n默认: false",
|
||||
"markdownDescription": "弹幕录制 \n默认: `false `\n\n是否录制弹幕,`true` 为录制,`false` 为不录制。\n\n本设置同时是所有“弹幕录制”的总开关,当本设置为 `false` 时其他所有“弹幕录制”设置无效,不会写入弹幕XML文件。",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmakuRaw": {
|
||||
"description": "弹幕录制-原始数据\n默认: false",
|
||||
"markdownDescription": "弹幕录制-原始数据 \n默认: `false `\n\n是否记录原始 JSON 数据。\n\n弹幕原始数据会保存到 XML 文件每一条弹幕数据的 `raw` attribute 上。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmakuSuperChat": {
|
||||
"description": "弹幕录制-SuperChat\n默认: true",
|
||||
"markdownDescription": "弹幕录制-SuperChat \n默认: `true `\n\n是否记录 SuperChat。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmakuGift": {
|
||||
"description": "弹幕录制-礼物\n默认: false",
|
||||
"markdownDescription": "弹幕录制-礼物 \n默认: `false `\n\n是否记录礼物。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordDanmakuGuard": {
|
||||
"description": "弹幕录制-上船\n默认: true",
|
||||
"markdownDescription": "弹幕录制-上船 \n默认: `true `\n\n是否记录上船(购买舰长)。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"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",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -92,12 +92,12 @@ export const data: Array<ConfigEntry> = [
|
|||
markdown: "录制的直播画质 qn 值,以英文逗号分割,靠前的优先。\n\n**注意**(从录播姬 1.3.10 开始):\n\n- 所有主播刚开播时都是只有“原画”的,如果选择不录原画会导致直播开头漏录。\n- 如果设置的录制画质里没有原画,但是主播只有原画画质,会导致不能录制直播。\n- 录播姬不会为了切换录制的画质主动断开录制。\n\n画质 | qn 值\n:--:|:--:\n4K | 20000\n原画 | 10000\n蓝光(杜比) | 401\n蓝光 | 400\n超清 | 250\n高清 | 150\n流畅 | 80"
|
||||
},
|
||||
{
|
||||
name: "RecordFilenameFormat",
|
||||
description: "录制文件名格式",
|
||||
name: "FileNameRecordTemplate",
|
||||
description: "录制文件名模板",
|
||||
type: "string?",
|
||||
configType: "globalOnly",
|
||||
defaultValue: "@\"{roomid}-{name}/录制-{roomid}-{date}-{time}-{ms}-{title}.flv\"",
|
||||
markdown: "- 只支持 FLV 格式\n- 所有大括号均为英文半角括号\n- 录制时如果出现文件名冲突,会使用一个默认文件名\n\n变量 | 含义\n:--:|:--:\n{date} | 当前日期(年月日)\n{time} | 当前时间(时分秒)\n{ms} | 当前时间毫秒\n{roomid} | 房间号\n{title} | 标题\n{name} | 主播名\n{parea} | 大分区\n{area} | 子分区\n{random} | 随机数字\n"
|
||||
markdown: "TODO: config v3 新的文件名模板系统的文档还没有写"
|
||||
},
|
||||
{
|
||||
name: "WebHookUrls",
|
||||
|
|
|
@ -22,7 +22,7 @@ interface SectionInfo {
|
|||
|
||||
const map: SectionInfoMap = {
|
||||
core: {
|
||||
path: './BililiveRecorder.Core/Config/V2/Config.gen.cs',
|
||||
path: './BililiveRecorder.Core/Config/V3/Config.gen.cs',
|
||||
format: true,
|
||||
header: true,
|
||||
build: builderCore
|
||||
|
@ -40,7 +40,7 @@ const map: SectionInfoMap = {
|
|||
build: builderWeb
|
||||
},
|
||||
schema: {
|
||||
path: './configV2.schema.json',
|
||||
path: './configV3.schema.json',
|
||||
format: false,
|
||||
header: false,
|
||||
build: builderSchema
|
||||
|
|
|
@ -4,7 +4,7 @@ import { trimEnd } from "../utils";
|
|||
export default function (data: ConfigEntry[]): string {
|
||||
let result = `using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using BililiveRecorder.Core.Config.V2;
|
||||
using BililiveRecorder.Core.Config.V3;
|
||||
|
||||
namespace BililiveRecorder.Cli.Configure
|
||||
{`;
|
||||
|
|
|
@ -7,7 +7,7 @@ using HierarchicalPropertyDefault;
|
|||
using Newtonsoft.Json;
|
||||
|
||||
#nullable enable
|
||||
namespace BililiveRecorder.Core.Config.V2
|
||||
namespace BililiveRecorder.Core.Config.V3
|
||||
{
|
||||
`;
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user