feat: split recording on title change

* feat:add CuttingByTitle to BililiveRecorder.Core.Config.V3

* add: CuttingByTitle

* add: new config-related test

* fix:test txt

* fix:test txt

* add: translations

* add:translations

* Reactivating git submodule webui

* revert webui submodule update

---------

Co-authored-by: genteure <genteure@gmail.com>
This commit is contained in:
cxumol 2023-10-12 02:59:45 -07:00 committed by GitHub
parent 26e97581df
commit 86e6526f09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 108 additions and 6 deletions

View File

@ -17,6 +17,7 @@ namespace BililiveRecorder.Cli.Configure
RecordMode,
CuttingMode,
CuttingNumber,
CuttingByTitle,
RecordDanmaku,
RecordDanmakuRaw,
RecordDanmakuSuperChat,
@ -56,6 +57,7 @@ namespace BililiveRecorder.Cli.Configure
RecordMode,
CuttingMode,
CuttingNumber,
CuttingByTitle,
RecordDanmaku,
RecordDanmakuRaw,
RecordDanmakuSuperChat,
@ -75,6 +77,7 @@ namespace BililiveRecorder.Cli.Configure
GlobalConfig.Add(GlobalConfigProperties.RecordMode, new ConfigInstruction<GlobalConfig, RecordMode>(config => config.HasRecordMode = false, (config, value) => config.RecordMode = value) { Name = "RecordMode", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.CuttingMode, new ConfigInstruction<GlobalConfig, CuttingMode>(config => config.HasCuttingMode = false, (config, value) => config.CuttingMode = value) { Name = "CuttingMode", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.CuttingNumber, new ConfigInstruction<GlobalConfig, uint>(config => config.HasCuttingNumber = false, (config, value) => config.CuttingNumber = value) { Name = "CuttingNumber", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.CuttingByTitle, new ConfigInstruction<GlobalConfig, bool>(config => config.HasCuttingByTitle = false, (config, value) => config.CuttingByTitle = value) { Name = "CuttingByTitle", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.RecordDanmaku, new ConfigInstruction<GlobalConfig, bool>(config => config.HasRecordDanmaku = false, (config, value) => config.RecordDanmaku = value) { Name = "RecordDanmaku", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.RecordDanmakuRaw, new ConfigInstruction<GlobalConfig, bool>(config => config.HasRecordDanmakuRaw = false, (config, value) => config.RecordDanmakuRaw = value) { Name = "RecordDanmakuRaw", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.RecordDanmakuSuperChat, new ConfigInstruction<GlobalConfig, bool>(config => config.HasRecordDanmakuSuperChat = false, (config, value) => config.RecordDanmakuSuperChat = value) { Name = "RecordDanmakuSuperChat", CanBeOptional = true });
@ -110,6 +113,7 @@ namespace BililiveRecorder.Cli.Configure
RoomConfig.Add(RoomConfigProperties.RecordMode, new ConfigInstruction<RoomConfig, RecordMode>(config => config.HasRecordMode = false, (config, value) => config.RecordMode = value) { Name = "RecordMode", CanBeOptional = true });
RoomConfig.Add(RoomConfigProperties.CuttingMode, new ConfigInstruction<RoomConfig, CuttingMode>(config => config.HasCuttingMode = false, (config, value) => config.CuttingMode = value) { Name = "CuttingMode", CanBeOptional = true });
RoomConfig.Add(RoomConfigProperties.CuttingNumber, new ConfigInstruction<RoomConfig, uint>(config => config.HasCuttingNumber = false, (config, value) => config.CuttingNumber = value) { Name = "CuttingNumber", CanBeOptional = true });
RoomConfig.Add(RoomConfigProperties.CuttingByTitle, new ConfigInstruction<RoomConfig, bool>(config => config.HasCuttingByTitle = false, (config, value) => config.CuttingByTitle = value) { Name = "CuttingByTitle", CanBeOptional = true });
RoomConfig.Add(RoomConfigProperties.RecordDanmaku, new ConfigInstruction<RoomConfig, bool>(config => config.HasRecordDanmaku = false, (config, value) => config.RecordDanmaku = value) { Name = "RecordDanmaku", CanBeOptional = true });
RoomConfig.Add(RoomConfigProperties.RecordDanmakuRaw, new ConfigInstruction<RoomConfig, bool>(config => config.HasRecordDanmakuRaw = false, (config, value) => config.RecordDanmakuRaw = value) { Name = "RecordDanmakuRaw", CanBeOptional = true });
RoomConfig.Add(RoomConfigProperties.RecordDanmakuSuperChat, new ConfigInstruction<RoomConfig, bool>(config => config.HasRecordDanmakuSuperChat = false, (config, value) => config.RecordDanmakuSuperChat = value) { Name = "RecordDanmakuSuperChat", CanBeOptional = true });

View File

@ -53,6 +53,14 @@ namespace BililiveRecorder.Core.Config.V3
[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 CuttingByTitle { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
public bool HasCuttingByTitle { get => this.GetPropertyHasValue(nameof(this.CuttingByTitle)); set => this.SetPropertyHasValue<bool>(value, nameof(this.CuttingByTitle)); }
[JsonProperty(nameof(CuttingByTitle)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<bool> OptionalCuttingByTitle { get => this.GetPropertyValueOptional<bool>(nameof(this.CuttingByTitle)); set => this.SetPropertyValueOptional(value, nameof(this.CuttingByTitle)); }
/// <summary>
/// 弹幕录制
/// </summary>
@ -251,6 +259,14 @@ namespace BililiveRecorder.Core.Config.V3
[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 CuttingByTitle { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
public bool HasCuttingByTitle { get => this.GetPropertyHasValue(nameof(this.CuttingByTitle)); set => this.SetPropertyHasValue<bool>(value, nameof(this.CuttingByTitle)); }
[JsonProperty(nameof(CuttingByTitle)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<bool> OptionalCuttingByTitle { get => this.GetPropertyValueOptional<bool>(nameof(this.CuttingByTitle)); set => this.SetPropertyValueOptional(value, nameof(this.CuttingByTitle)); }
/// <summary>
/// 弹幕录制
/// </summary>
@ -496,6 +512,8 @@ namespace BililiveRecorder.Core.Config.V3
public uint CuttingNumber => 100;
public bool CuttingByTitle => false;
public bool RecordDanmaku => false;
public bool RecordDanmakuRaw => false;

View File

@ -669,6 +669,11 @@ retry:
this.AutoRecordForThisSession = true;
}
break;
case nameof(this.Title):
if (this.RoomConfig.CuttingByTitle){
this.SplitOutput();
}
break;
default:
break;
}

View File

@ -96,6 +96,7 @@
<TextBlock VerticalAlignment="Center" Text="{l:Loc Settings_Splitting_TextBox_Right}"/>
</StackPanel>
</local:SettingWithDefault>
<ui:ToggleSwitch IsOn="{Binding CuttingByTitle}" OnContent="{l:Loc Settings_Splitting_ToggleSwitch_ByTitle}" OffContent="{l:Loc Settings_Splitting_ToggleSwitch_ByTitle}"/>
</StackPanel>
</GroupBox>
<GroupBox Header="直播封面">

View File

@ -78,6 +78,9 @@
<TextBlock Text="{l:Loc Settings_Splitting_TextBox_TimeUnit}" Visibility="{Binding ElementName=CutByTimeRadioButton,Path=IsChecked,Converter={StaticResource BooleanToVisibilityCollapsedConverter}}"/>
<TextBlock Text="{l:Loc Settings_Splitting_TextBox_Right}"/>
</StackPanel>
<ui:ToggleSwitch IsOn="{Binding CuttingByTitle}" OnContent="{l:Loc Settings_Splitting_ToggleSwitch_ByTitle}" OffContent="{l:Loc Settings_Splitting_ToggleSwitch_ByTitle}"/>
</StackPanel>
</GroupBox>
<GroupBox Header="直播封面">

View File

@ -490,7 +490,10 @@ Only FLV is supported</value>
<value>Split recording by video time</value>
</data>
<data name="Settings_Splitting_RadioButton_Disabled" xml:space="preserve">
<value>Disable</value>
<value>Don't split by file size or video time</value>
</data>
<data name="Settings_Splitting_ToggleSwitch_ByTitle" xml:space="preserve">
<value>Split recording by change of live stream title</value>
</data>
<data name="Settings_Splitting_TextBox_Left" xml:space="preserve">
<value>Split recording every</value>

View File

@ -490,7 +490,10 @@ FLV フォーマットのみサポートされています。</value>
<value>録画の長さによって分割する</value>
</data>
<data name="Settings_Splitting_RadioButton_Disabled" xml:space="preserve">
<value>自動分割しない</value>
<value>ファイルサイズやビデオの時間によって分割しないでください</value>
</data>
<data name="Settings_Splitting_ToggleSwitch_ByTitle" xml:space="preserve">
<value>配信ルームの名を変更した後に、録画を分割する</value>
</data>
<data name="Settings_Splitting_TextBox_Left" xml:space="preserve">
<value> </value>

View File

@ -490,7 +490,10 @@
<value>根据视频时间自动分段</value>
</data>
<data name="Settings_Splitting_RadioButton_Disabled" xml:space="preserve">
<value>不自动分段</value>
<value>不要根据文件大小/视频时间自动分段</value>
</data>
<data name="Settings_Splitting_ToggleSwitch_ByTitle" xml:space="preserve">
<value>直播间更改标题后自动分段</value>
</data>
<data name="Settings_Splitting_TextBox_Left" xml:space="preserve">
<value>每</value>

View File

@ -457,13 +457,16 @@
<value>設定</value>
</data>
<data name="Settings_Splitting_RadioButton_BySize" xml:space="preserve">
<value>依照文件大小自動分段</value>
<value>依照檔案大小自動分段</value>
</data>
<data name="Settings_Splitting_RadioButton_ByTime" xml:space="preserve">
<value>依照影片錄製時自動分段</value>
<value>依照影片錄製時自動分段</value>
</data>
<data name="Settings_Splitting_RadioButton_Disabled" xml:space="preserve">
<value>不自動分段</value>
<value>不要根據檔案大小或影片時長拆分錄製</value>
</data>
<data name="Settings_Splitting_ToggleSwitch_ByTitle" xml:space="preserve">
<value>按直播間標題变更拆分錄製</value>
</data>
<data name="Settings_Splitting_TextBox_Right" xml:space="preserve">

View File

@ -16,6 +16,7 @@ namespace BililiveRecorder.Web.Models
public Optional<RecordMode>? OptionalRecordMode { get; set; }
public Optional<CuttingMode>? OptionalCuttingMode { get; set; }
public Optional<uint>? OptionalCuttingNumber { get; set; }
public Optional<bool>? OptionalCuttingByTitle { get; set; }
public Optional<bool>? OptionalRecordDanmaku { get; set; }
public Optional<bool>? OptionalRecordDanmakuRaw { get; set; }
public Optional<bool>? OptionalRecordDanmakuSuperChat { get; set; }
@ -31,6 +32,7 @@ namespace BililiveRecorder.Web.Models
if (this.OptionalRecordMode.HasValue) config.OptionalRecordMode = this.OptionalRecordMode.Value;
if (this.OptionalCuttingMode.HasValue) config.OptionalCuttingMode = this.OptionalCuttingMode.Value;
if (this.OptionalCuttingNumber.HasValue) config.OptionalCuttingNumber = this.OptionalCuttingNumber.Value;
if (this.OptionalCuttingByTitle.HasValue) config.OptionalCuttingByTitle = this.OptionalCuttingByTitle.Value;
if (this.OptionalRecordDanmaku.HasValue) config.OptionalRecordDanmaku = this.OptionalRecordDanmaku.Value;
if (this.OptionalRecordDanmakuRaw.HasValue) config.OptionalRecordDanmakuRaw = this.OptionalRecordDanmakuRaw.Value;
if (this.OptionalRecordDanmakuSuperChat.HasValue) config.OptionalRecordDanmakuSuperChat = this.OptionalRecordDanmakuSuperChat.Value;
@ -47,6 +49,7 @@ namespace BililiveRecorder.Web.Models
public Optional<RecordMode>? OptionalRecordMode { get; set; }
public Optional<CuttingMode>? OptionalCuttingMode { get; set; }
public Optional<uint>? OptionalCuttingNumber { get; set; }
public Optional<bool>? OptionalCuttingByTitle { get; set; }
public Optional<bool>? OptionalRecordDanmaku { get; set; }
public Optional<bool>? OptionalRecordDanmakuRaw { get; set; }
public Optional<bool>? OptionalRecordDanmakuSuperChat { get; set; }
@ -82,6 +85,7 @@ namespace BililiveRecorder.Web.Models
if (this.OptionalRecordMode.HasValue) config.OptionalRecordMode = this.OptionalRecordMode.Value;
if (this.OptionalCuttingMode.HasValue) config.OptionalCuttingMode = this.OptionalCuttingMode.Value;
if (this.OptionalCuttingNumber.HasValue) config.OptionalCuttingNumber = this.OptionalCuttingNumber.Value;
if (this.OptionalCuttingByTitle.HasValue) config.OptionalCuttingByTitle = this.OptionalCuttingByTitle.Value;
if (this.OptionalRecordDanmaku.HasValue) config.OptionalRecordDanmaku = this.OptionalRecordDanmaku.Value;
if (this.OptionalRecordDanmakuRaw.HasValue) config.OptionalRecordDanmakuRaw = this.OptionalRecordDanmakuRaw.Value;
if (this.OptionalRecordDanmakuSuperChat.HasValue) config.OptionalRecordDanmakuSuperChat = this.OptionalRecordDanmakuSuperChat.Value;
@ -124,6 +128,7 @@ namespace BililiveRecorder.Web.Models.Rest
public Optional<RecordMode> OptionalRecordMode { get; set; }
public Optional<CuttingMode> OptionalCuttingMode { get; set; }
public Optional<uint> OptionalCuttingNumber { get; set; }
public Optional<bool> OptionalCuttingByTitle { get; set; }
public Optional<bool> OptionalRecordDanmaku { get; set; }
public Optional<bool> OptionalRecordDanmakuRaw { get; set; }
public Optional<bool> OptionalRecordDanmakuSuperChat { get; set; }
@ -139,6 +144,7 @@ namespace BililiveRecorder.Web.Models.Rest
public Optional<RecordMode> OptionalRecordMode { get; set; }
public Optional<CuttingMode> OptionalCuttingMode { get; set; }
public Optional<uint> OptionalCuttingNumber { get; set; }
public Optional<bool> OptionalCuttingByTitle { get; set; }
public Optional<bool> OptionalRecordDanmaku { get; set; }
public Optional<bool> OptionalRecordDanmakuRaw { get; set; }
public Optional<bool> OptionalRecordDanmakuSuperChat { get; set; }
@ -183,6 +189,7 @@ namespace BililiveRecorder.Web.Models.Graphql
this.Field(x => x.OptionalRecordMode, type: typeof(HierarchicalOptionalType<RecordMode>));
this.Field(x => x.OptionalCuttingMode, type: typeof(HierarchicalOptionalType<CuttingMode>));
this.Field(x => x.OptionalCuttingNumber, type: typeof(HierarchicalOptionalType<uint>));
this.Field(x => x.OptionalCuttingByTitle, type: typeof(HierarchicalOptionalType<bool>));
this.Field(x => x.OptionalRecordDanmaku, type: typeof(HierarchicalOptionalType<bool>));
this.Field(x => x.OptionalRecordDanmakuRaw, type: typeof(HierarchicalOptionalType<bool>));
this.Field(x => x.OptionalRecordDanmakuSuperChat, type: typeof(HierarchicalOptionalType<bool>));
@ -201,6 +208,7 @@ namespace BililiveRecorder.Web.Models.Graphql
this.Field(x => x.OptionalRecordMode, type: typeof(HierarchicalOptionalType<RecordMode>));
this.Field(x => x.OptionalCuttingMode, type: typeof(HierarchicalOptionalType<CuttingMode>));
this.Field(x => x.OptionalCuttingNumber, type: typeof(HierarchicalOptionalType<uint>));
this.Field(x => x.OptionalCuttingByTitle, type: typeof(HierarchicalOptionalType<bool>));
this.Field(x => x.OptionalRecordDanmaku, type: typeof(HierarchicalOptionalType<bool>));
this.Field(x => x.OptionalRecordDanmakuRaw, type: typeof(HierarchicalOptionalType<bool>));
this.Field(x => x.OptionalRecordDanmakuSuperChat, type: typeof(HierarchicalOptionalType<bool>));
@ -240,6 +248,7 @@ namespace BililiveRecorder.Web.Models.Graphql
this.Field(x => x.RecordMode);
this.Field(x => x.CuttingMode);
this.Field(x => x.CuttingNumber);
this.Field(x => x.CuttingByTitle);
this.Field(x => x.RecordDanmaku);
this.Field(x => x.RecordDanmakuRaw);
this.Field(x => x.RecordDanmakuSuperChat);
@ -280,6 +289,7 @@ namespace BililiveRecorder.Web.Models.Graphql
this.Field(x => x.OptionalRecordMode, nullable: true, type: typeof(HierarchicalOptionalInputType<RecordMode>));
this.Field(x => x.OptionalCuttingMode, nullable: true, type: typeof(HierarchicalOptionalInputType<CuttingMode>));
this.Field(x => x.OptionalCuttingNumber, nullable: true, type: typeof(HierarchicalOptionalInputType<uint>));
this.Field(x => x.OptionalCuttingByTitle, nullable: true, type: typeof(HierarchicalOptionalInputType<bool>));
this.Field(x => x.OptionalRecordDanmaku, nullable: true, type: typeof(HierarchicalOptionalInputType<bool>));
this.Field(x => x.OptionalRecordDanmakuRaw, nullable: true, type: typeof(HierarchicalOptionalInputType<bool>));
this.Field(x => x.OptionalRecordDanmakuSuperChat, nullable: true, type: typeof(HierarchicalOptionalInputType<bool>));
@ -298,6 +308,7 @@ namespace BililiveRecorder.Web.Models.Graphql
this.Field(x => x.OptionalRecordMode, nullable: true, type: typeof(HierarchicalOptionalInputType<RecordMode>));
this.Field(x => x.OptionalCuttingMode, nullable: true, type: typeof(HierarchicalOptionalInputType<CuttingMode>));
this.Field(x => x.OptionalCuttingNumber, nullable: true, type: typeof(HierarchicalOptionalInputType<uint>));
this.Field(x => x.OptionalCuttingByTitle, nullable: true, type: typeof(HierarchicalOptionalInputType<bool>));
this.Field(x => x.OptionalRecordDanmaku, nullable: true, type: typeof(HierarchicalOptionalInputType<bool>));
this.Field(x => x.OptionalRecordDanmakuRaw, nullable: true, type: typeof(HierarchicalOptionalInputType<bool>));
this.Field(x => x.OptionalRecordDanmakuSuperChat, nullable: true, type: typeof(HierarchicalOptionalInputType<bool>));

View File

@ -434,6 +434,22 @@
}
}
},
"CuttingByTitle": {
"description": "改标题后自动分段\n默认: false",
"markdownDescription": "改标题后自动分段 \n默认: `false `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
"HasValue": {
"type": "boolean",
"default": true
},
"Value": {
"type": "boolean",
"default": false
}
}
},
"RecordDanmaku": {
"description": "弹幕录制\n默认: false",
"markdownDescription": "弹幕录制 \n默认: `false `\n\n",
@ -663,6 +679,22 @@
}
}
},
"CuttingByTitle": {
"description": "改标题后自动分段\n默认: false",
"markdownDescription": "改标题后自动分段 \n默认: `false `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
"HasValue": {
"type": "boolean",
"default": true
},
"Value": {
"type": "boolean",
"default": false
}
}
},
"RecordDanmaku": {
"description": "弹幕录制\n默认: false",
"markdownDescription": "弹幕录制 \n默认: `false `\n\n",

View File

@ -37,6 +37,13 @@ export const data: Array<ConfigEntry> = [
configType: "room",
default: 100
},
{
id: "CuttingByTitle",
name: "改标题后自动分段",
type: "bool",
configType: "room",
default: false
},
{
id: "RecordDanmaku",
name: "弹幕录制",

View File

@ -74,6 +74,7 @@ namespace BililiveRecorder.Core.Config.V3
{
public static readonly BililiveRecorder.Core.Config.V3.DefaultConfig Instance;
public string Cookie { get; }
public bool CuttingByTitle { get; }
public BililiveRecorder.Core.Config.CuttingMode CuttingMode { get; }
public uint CuttingNumber { get; }
public bool DanmakuAuthenticateWithStreamerUid { get; }
@ -111,6 +112,7 @@ namespace BililiveRecorder.Core.Config.V3
{
public GlobalConfig() { }
public string? Cookie { get; set; }
public bool CuttingByTitle { get; set; }
public BililiveRecorder.Core.Config.CuttingMode CuttingMode { get; set; }
public uint CuttingNumber { get; set; }
public bool DanmakuAuthenticateWithStreamerUid { get; set; }
@ -119,6 +121,7 @@ namespace BililiveRecorder.Core.Config.V3
public bool FlvProcessorSplitOnScriptTag { get; set; }
public bool FlvWriteMetadata { get; set; }
public bool HasCookie { get; set; }
public bool HasCuttingByTitle { get; set; }
public bool HasCuttingMode { get; set; }
public bool HasCuttingNumber { get; set; }
public bool HasDanmakuAuthenticateWithStreamerUid { get; set; }
@ -155,6 +158,8 @@ namespace BililiveRecorder.Core.Config.V3
public bool NetworkTransportUseSystemProxy { get; set; }
[Newtonsoft.Json.JsonProperty("Cookie")]
public HierarchicalPropertyDefault.Optional<string?> OptionalCookie { get; set; }
[Newtonsoft.Json.JsonProperty("CuttingByTitle")]
public HierarchicalPropertyDefault.Optional<bool> OptionalCuttingByTitle { get; set; }
[Newtonsoft.Json.JsonProperty("CuttingMode")]
public HierarchicalPropertyDefault.Optional<BililiveRecorder.Core.Config.CuttingMode> OptionalCuttingMode { get; set; }
[Newtonsoft.Json.JsonProperty("CuttingNumber")]
@ -251,6 +256,7 @@ namespace BililiveRecorder.Core.Config.V3
public RoomConfig() { }
public bool AutoRecord { get; set; }
public string? Cookie { get; }
public bool CuttingByTitle { get; set; }
public BililiveRecorder.Core.Config.CuttingMode CuttingMode { get; set; }
public uint CuttingNumber { get; set; }
public bool DanmakuAuthenticateWithStreamerUid { get; }
@ -259,6 +265,7 @@ namespace BililiveRecorder.Core.Config.V3
public bool FlvProcessorSplitOnScriptTag { get; set; }
public bool FlvWriteMetadata { get; }
public bool HasAutoRecord { get; set; }
public bool HasCuttingByTitle { get; set; }
public bool HasCuttingMode { get; set; }
public bool HasCuttingNumber { get; set; }
public bool HasFlvProcessorSplitOnScriptTag { get; set; }
@ -276,6 +283,8 @@ namespace BililiveRecorder.Core.Config.V3
public bool NetworkTransportUseSystemProxy { get; }
[Newtonsoft.Json.JsonProperty("AutoRecord")]
public HierarchicalPropertyDefault.Optional<bool> OptionalAutoRecord { get; set; }
[Newtonsoft.Json.JsonProperty("CuttingByTitle")]
public HierarchicalPropertyDefault.Optional<bool> OptionalCuttingByTitle { get; set; }
[Newtonsoft.Json.JsonProperty("CuttingMode")]
public HierarchicalPropertyDefault.Optional<BililiveRecorder.Core.Config.CuttingMode> OptionalCuttingMode { get; set; }
[Newtonsoft.Json.JsonProperty("CuttingNumber")]