mirror of
https://github.com/BililiveRecorder/BililiveRecorder.git
synced 2024-11-16 11:42:22 +08:00
Core: Strictly enforce qn settings
This commit is contained in:
parent
ccdba838f8
commit
06a4c59bb7
|
@ -30,6 +30,7 @@ namespace BililiveRecorder.Cli.Configure
|
||||||
LiveApiHost,
|
LiveApiHost,
|
||||||
TimingCheckInterval,
|
TimingCheckInterval,
|
||||||
TimingStreamRetry,
|
TimingStreamRetry,
|
||||||
|
TimingStreamRetryNoQn,
|
||||||
TimingStreamConnect,
|
TimingStreamConnect,
|
||||||
TimingDanmakuRetry,
|
TimingDanmakuRetry,
|
||||||
TimingWatchdogTimeout,
|
TimingWatchdogTimeout,
|
||||||
|
@ -75,6 +76,7 @@ namespace BililiveRecorder.Cli.Configure
|
||||||
GlobalConfig.Add(GlobalConfigProperties.LiveApiHost, new ConfigInstruction<GlobalConfig, string>(config => config.HasLiveApiHost = false, (config, value) => config.LiveApiHost = value) { Name = "LiveApiHost", CanBeOptional = true });
|
GlobalConfig.Add(GlobalConfigProperties.LiveApiHost, new ConfigInstruction<GlobalConfig, string>(config => config.HasLiveApiHost = false, (config, value) => config.LiveApiHost = value) { Name = "LiveApiHost", CanBeOptional = true });
|
||||||
GlobalConfig.Add(GlobalConfigProperties.TimingCheckInterval, new ConfigInstruction<GlobalConfig, uint>(config => config.HasTimingCheckInterval = false, (config, value) => config.TimingCheckInterval = value) { Name = "TimingCheckInterval", CanBeOptional = true });
|
GlobalConfig.Add(GlobalConfigProperties.TimingCheckInterval, new ConfigInstruction<GlobalConfig, uint>(config => config.HasTimingCheckInterval = false, (config, value) => config.TimingCheckInterval = value) { Name = "TimingCheckInterval", CanBeOptional = true });
|
||||||
GlobalConfig.Add(GlobalConfigProperties.TimingStreamRetry, new ConfigInstruction<GlobalConfig, uint>(config => config.HasTimingStreamRetry = false, (config, value) => config.TimingStreamRetry = value) { Name = "TimingStreamRetry", CanBeOptional = true });
|
GlobalConfig.Add(GlobalConfigProperties.TimingStreamRetry, new ConfigInstruction<GlobalConfig, uint>(config => config.HasTimingStreamRetry = false, (config, value) => config.TimingStreamRetry = value) { Name = "TimingStreamRetry", CanBeOptional = true });
|
||||||
|
GlobalConfig.Add(GlobalConfigProperties.TimingStreamRetryNoQn, new ConfigInstruction<GlobalConfig, uint>(config => config.HasTimingStreamRetryNoQn = false, (config, value) => config.TimingStreamRetryNoQn = value) { Name = "TimingStreamRetryNoQn", CanBeOptional = true });
|
||||||
GlobalConfig.Add(GlobalConfigProperties.TimingStreamConnect, new ConfigInstruction<GlobalConfig, uint>(config => config.HasTimingStreamConnect = false, (config, value) => config.TimingStreamConnect = value) { Name = "TimingStreamConnect", CanBeOptional = true });
|
GlobalConfig.Add(GlobalConfigProperties.TimingStreamConnect, new ConfigInstruction<GlobalConfig, uint>(config => config.HasTimingStreamConnect = false, (config, value) => config.TimingStreamConnect = value) { Name = "TimingStreamConnect", CanBeOptional = true });
|
||||||
GlobalConfig.Add(GlobalConfigProperties.TimingDanmakuRetry, new ConfigInstruction<GlobalConfig, uint>(config => config.HasTimingDanmakuRetry = false, (config, value) => config.TimingDanmakuRetry = value) { Name = "TimingDanmakuRetry", CanBeOptional = true });
|
GlobalConfig.Add(GlobalConfigProperties.TimingDanmakuRetry, new ConfigInstruction<GlobalConfig, uint>(config => config.HasTimingDanmakuRetry = false, (config, value) => config.TimingDanmakuRetry = value) { Name = "TimingDanmakuRetry", CanBeOptional = true });
|
||||||
GlobalConfig.Add(GlobalConfigProperties.TimingWatchdogTimeout, new ConfigInstruction<GlobalConfig, uint>(config => config.HasTimingWatchdogTimeout = false, (config, value) => config.TimingWatchdogTimeout = value) { Name = "TimingWatchdogTimeout", CanBeOptional = true });
|
GlobalConfig.Add(GlobalConfigProperties.TimingWatchdogTimeout, new ConfigInstruction<GlobalConfig, uint>(config => config.HasTimingWatchdogTimeout = false, (config, value) => config.TimingWatchdogTimeout = value) { Name = "TimingWatchdogTimeout", CanBeOptional = true });
|
||||||
|
|
|
@ -141,6 +141,11 @@ namespace BililiveRecorder.Core.Config.V2
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint TimingStreamRetry => this.GetPropertyValue<uint>();
|
public uint TimingStreamRetry => this.GetPropertyValue<uint>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 录制无指定画质重连时间间隔 秒
|
||||||
|
/// </summary>
|
||||||
|
public uint TimingStreamRetryNoQn => this.GetPropertyValue<uint>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 连接直播服务器超时时间 毫秒
|
/// 连接直播服务器超时时间 毫秒
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -302,6 +307,14 @@ namespace BililiveRecorder.Core.Config.V2
|
||||||
[JsonProperty(nameof(TimingStreamRetry)), EditorBrowsable(EditorBrowsableState.Never)]
|
[JsonProperty(nameof(TimingStreamRetry)), EditorBrowsable(EditorBrowsableState.Never)]
|
||||||
public Optional<uint> OptionalTimingStreamRetry { get => this.GetPropertyValueOptional<uint>(nameof(this.TimingStreamRetry)); set => this.SetPropertyValueOptional(value, nameof(this.TimingStreamRetry)); }
|
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>
|
||||||
/// 连接直播服务器超时时间 毫秒
|
/// 连接直播服务器超时时间 毫秒
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -375,6 +388,8 @@ namespace BililiveRecorder.Core.Config.V2
|
||||||
|
|
||||||
public uint TimingStreamRetry => 6 * 1000;
|
public uint TimingStreamRetry => 6 * 1000;
|
||||||
|
|
||||||
|
public uint TimingStreamRetryNoQn => 90;
|
||||||
|
|
||||||
public uint TimingStreamConnect => 5 * 1000;
|
public uint TimingStreamConnect => 5 * 1000;
|
||||||
|
|
||||||
public uint TimingDanmakuRetry => 9 * 1000;
|
public uint TimingDanmakuRetry => 9 * 1000;
|
||||||
|
|
24
BililiveRecorder.Core/NoMatchingQnValueException.cs
Normal file
24
BililiveRecorder.Core/NoMatchingQnValueException.cs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace BililiveRecorder.Core
|
||||||
|
{
|
||||||
|
public class NoMatchingQnValueException : Exception
|
||||||
|
{
|
||||||
|
public NoMatchingQnValueException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public NoMatchingQnValueException(string message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public NoMatchingQnValueException(string message, Exception innerException) : base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected NoMatchingQnValueException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -238,54 +238,46 @@ namespace BililiveRecorder.Core.Recording
|
||||||
{
|
{
|
||||||
const int DefaultQn = 10000;
|
const int DefaultQn = 10000;
|
||||||
var selected_qn = DefaultQn;
|
var selected_qn = DefaultQn;
|
||||||
int[] qns;
|
|
||||||
Api.Model.RoomPlayInfo.UrlInfoItem[]? url_infos;
|
|
||||||
|
|
||||||
var codecItem = await this.apiClient.GetCodecItemInStreamUrlAsync(roomid: roomid, qn: DefaultQn).ConfigureAwait(false);
|
var codecItem = await this.apiClient.GetCodecItemInStreamUrlAsync(roomid: roomid, qn: DefaultQn).ConfigureAwait(false);
|
||||||
|
|
||||||
if (codecItem is null)
|
if (codecItem is null)
|
||||||
throw new Exception("no supported stream url, qn: " + DefaultQn);
|
throw new Exception("no supported stream url, qn: " + DefaultQn);
|
||||||
|
|
||||||
|
var qns = this.room.RoomConfig.RecordingQuality?.Split(new[] { ',', ',', '、', ' ' }, StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(x => int.TryParse(x, out var num) ? num : -1)
|
||||||
|
.Where(x => x > 0)
|
||||||
|
.ToArray()
|
||||||
|
?? Array.Empty<int>();
|
||||||
|
|
||||||
|
// Select first avaiable qn
|
||||||
|
foreach (var qn in qns)
|
||||||
{
|
{
|
||||||
try
|
if (codecItem.AcceptQn.Contains(qn))
|
||||||
{
|
{
|
||||||
qns = this.room.RoomConfig.RecordingQuality.Split(new[] { ',', ',', '、', ' ' }, StringSplitOptions.RemoveEmptyEntries)
|
selected_qn = qn;
|
||||||
.Select(x => int.TryParse(x, out var num) ? num : -1)
|
goto match_qn_success;
|
||||||
.Where(x => x > 0)
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
foreach (var qn in qns)
|
|
||||||
{
|
|
||||||
if (codecItem.AcceptQn.Contains(qn))
|
|
||||||
{
|
|
||||||
selected_qn = qn;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.Debug("设置画质 {QnSettings}, 可用画质 {AcceptQn}, 最终选择 {SelectedQn}", qns, codecItem.AcceptQn, selected_qn);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
this.logger.Warning(ex, "判断录制画质时出错,将默认使用 原画(10000)");
|
|
||||||
qns = new[] { DefaultQn };
|
|
||||||
url_infos = codecItem.UrlInfos;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.Information("没有符合设置要求的画质,稍后再试。设置画质 {QnSettings}, 可用画质 {AcceptQn}", qns, codecItem.AcceptQn);
|
||||||
|
throw new NoMatchingQnValueException();
|
||||||
|
|
||||||
|
match_qn_success:
|
||||||
|
this.logger.Debug("设置画质 {QnSettings}, 可用画质 {AcceptQn}, 最终选择 {SelectedQn}", qns, codecItem.AcceptQn, selected_qn);
|
||||||
|
|
||||||
if (selected_qn != DefaultQn)
|
if (selected_qn != DefaultQn)
|
||||||
{
|
{
|
||||||
// 最终选择的 qn 与默认不同,需要重新请求一次
|
// 最终选择的 qn 与默认不同,需要重新请求一次
|
||||||
codecItem = await this.apiClient.GetCodecItemInStreamUrlAsync(roomid: roomid, qn: selected_qn).ConfigureAwait(false);
|
codecItem = await this.apiClient.GetCodecItemInStreamUrlAsync(roomid: roomid, qn: selected_qn).ConfigureAwait(false);
|
||||||
|
|
||||||
if (codecItem is null)
|
if (codecItem is null)
|
||||||
throw new Exception("no supported stream url, qn: " + DefaultQn);
|
throw new Exception("no supported stream url, qn: " + selected_qn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codecItem.CurrentQn != selected_qn || !qns.Contains(codecItem.CurrentQn))
|
if (codecItem.CurrentQn != selected_qn || !qns.Contains(codecItem.CurrentQn))
|
||||||
this.logger.Warning("当前录制的画质是 {CurrentQn}", codecItem.CurrentQn);
|
this.logger.Warning("当前录制的画质是 {CurrentQn}", codecItem.CurrentQn);
|
||||||
|
|
||||||
url_infos = codecItem.UrlInfos;
|
var url_infos = codecItem.UrlInfos;
|
||||||
if (url_infos is null || url_infos.Length == 0)
|
if (url_infos is null || url_infos.Length == 0)
|
||||||
throw new Exception("no url_info");
|
throw new Exception("no url_info");
|
||||||
|
|
||||||
|
|
15
BililiveRecorder.Core/RestartRecordingReason.cs
Normal file
15
BililiveRecorder.Core/RestartRecordingReason.cs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
namespace BililiveRecorder.Core
|
||||||
|
{
|
||||||
|
public enum RestartRecordingReason
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 普通重试
|
||||||
|
/// </summary>
|
||||||
|
GenericRetry,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 无对应直播画质
|
||||||
|
/// </summary>
|
||||||
|
NoMatchingQnValue,
|
||||||
|
}
|
||||||
|
}
|
|
@ -235,6 +235,16 @@ namespace BililiveRecorder.Core
|
||||||
{
|
{
|
||||||
await this.recordTask.StartAsync();
|
await this.recordTask.StartAsync();
|
||||||
}
|
}
|
||||||
|
catch (NoMatchingQnValueException)
|
||||||
|
{
|
||||||
|
this.recordTask = null;
|
||||||
|
this.OnPropertyChanged(nameof(this.Recording));
|
||||||
|
|
||||||
|
// 无匹配的画质,重试录制之前等待更长时间
|
||||||
|
_ = Task.Run(() => this.RestartAfterRecordTaskFailedAsync(RestartRecordingReason.NoMatchingQnValue));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
this.logger.Write(ex is ExecutionRejectedException ? LogEventLevel.Verbose : LogEventLevel.Warning, ex, "启动录制出错");
|
this.logger.Write(ex is ExecutionRejectedException ? LogEventLevel.Verbose : LogEventLevel.Warning, ex, "启动录制出错");
|
||||||
|
@ -243,10 +253,11 @@ namespace BililiveRecorder.Core
|
||||||
this.OnPropertyChanged(nameof(this.Recording));
|
this.OnPropertyChanged(nameof(this.Recording));
|
||||||
|
|
||||||
// 请求直播流出错时的重试逻辑
|
// 请求直播流出错时的重试逻辑
|
||||||
_ = Task.Run(this.RestartAfterRecordTaskFailedAsync);
|
_ = Task.Run(() => this.RestartAfterRecordTaskFailedAsync(RestartRecordingReason.GenericRetry));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RecordSessionStarted?.Invoke(this, new RecordSessionStartedEventArgs(this)
|
RecordSessionStarted?.Invoke(this, new RecordSessionStartedEventArgs(this)
|
||||||
{
|
{
|
||||||
SessionId = this.recordTask.SessionId
|
SessionId = this.recordTask.SessionId
|
||||||
|
@ -256,7 +267,7 @@ namespace BililiveRecorder.Core
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
private async Task RestartAfterRecordTaskFailedAsync()
|
private async Task RestartAfterRecordTaskFailedAsync(RestartRecordingReason restartRecordingReason)
|
||||||
{
|
{
|
||||||
if (this.disposedValue)
|
if (this.disposedValue)
|
||||||
return;
|
return;
|
||||||
|
@ -270,7 +281,13 @@ namespace BililiveRecorder.Core
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Task.Delay((int)this.RoomConfig.TimingStreamRetry, this.ct).ConfigureAwait(false);
|
var delay = restartRecordingReason switch
|
||||||
|
{
|
||||||
|
RestartRecordingReason.GenericRetry => this.RoomConfig.TimingStreamRetry,
|
||||||
|
RestartRecordingReason.NoMatchingQnValue => this.RoomConfig.TimingStreamRetryNoQn * 1000,
|
||||||
|
_ => throw new InvalidOperationException()
|
||||||
|
};
|
||||||
|
await Task.Delay((int)delay, this.ct).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException)
|
catch (TaskCanceledException)
|
||||||
{
|
{
|
||||||
|
@ -293,7 +310,7 @@ namespace BililiveRecorder.Core
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
this.logger.Write(ex is ExecutionRejectedException ? LogEventLevel.Verbose : LogEventLevel.Warning, ex, "重试开始录制时出错");
|
this.logger.Write(ex is ExecutionRejectedException ? LogEventLevel.Verbose : LogEventLevel.Warning, ex, "重试开始录制时出错");
|
||||||
_ = Task.Run(this.RestartAfterRecordTaskFailedAsync);
|
_ = Task.Run(() => this.RestartAfterRecordTaskFailedAsync(restartRecordingReason));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,7 +420,7 @@ namespace BililiveRecorder.Core
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
this.logger.Write(LogEventLevel.Warning, ex, "重试开始录制时出错");
|
this.logger.Write(LogEventLevel.Warning, ex, "重试开始录制时出错");
|
||||||
_ = Task.Run(this.RestartAfterRecordTaskFailedAsync);
|
_ = Task.Run(() => this.RestartAfterRecordTaskFailedAsync(RestartRecordingReason.GenericRetry));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,12 @@
|
||||||
</c:SettingWithDefault.ToolTip>
|
</c:SettingWithDefault.ToolTip>
|
||||||
<ui:NumberBox Minimum="1000" Description="单位: 毫秒" SmallChange="100" Text="{Binding TimingStreamRetry,Delay=500}"/>
|
<ui:NumberBox Minimum="1000" Description="单位: 毫秒" SmallChange="100" Text="{Binding TimingStreamRetry,Delay=500}"/>
|
||||||
</c:SettingWithDefault>
|
</c:SettingWithDefault>
|
||||||
|
<c:SettingWithDefault IsSettingNotUsingDefault="{Binding HasTimingStreamRetryNoQn}" Header="录制重试间隔(无匹配画质)">
|
||||||
|
<c:SettingWithDefault.ToolTip>
|
||||||
|
<TextBlock>无匹配的画质后等待多长时间再尝试开始录制</TextBlock>
|
||||||
|
</c:SettingWithDefault.ToolTip>
|
||||||
|
<ui:NumberBox Minimum="10" Description="单位: 秒" SmallChange="5" Text="{Binding TimingStreamRetryNoQn,Delay=500}"/>
|
||||||
|
</c:SettingWithDefault>
|
||||||
<c:SettingWithDefault IsSettingNotUsingDefault="{Binding HasTimingStreamConnect}" Header="录制连接超时">
|
<c:SettingWithDefault IsSettingNotUsingDefault="{Binding HasTimingStreamConnect}" Header="录制连接超时">
|
||||||
<c:SettingWithDefault.ToolTip>
|
<c:SettingWithDefault.ToolTip>
|
||||||
<TextBlock>
|
<TextBlock>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
"properties": {
|
"properties": {
|
||||||
"RecordFilenameFormat": {
|
"RecordFilenameFormat": {
|
||||||
"description": "录制文件名格式\n默认: {roomid}-{name}/录制-{roomid}-{date}-{time}-{ms}-{title}.flv",
|
"description": "录制文件名格式\n默认: {roomid}-{name}/录制-{roomid}-{date}-{time}-{ms}-{title}.flv",
|
||||||
"markdownDescription": "录制文件名格式 \n默认: `{roomid}-{name}/录制-{roomid}-{date}-{time}-{ms}-{title}.flv `\n\n",
|
"markdownDescription": "录制文件名格式 \n默认: `{roomid}-{name}/录制-{roomid}-{date}-{time}-{ms}-{title}.flv `\n\n- 只支持 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",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
},
|
},
|
||||||
"WebHookUrls": {
|
"WebHookUrls": {
|
||||||
"description": "WebhookV1\n默认: ",
|
"description": "WebhookV1\n默认: ",
|
||||||
"markdownDescription": "WebhookV1 \n默认: ` `\n\n",
|
"markdownDescription": "WebhookV1 \n默认: ` `\n\n具体文档见 [Webhook](/docs/basic/webhook/)",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -40,7 +40,7 @@
|
||||||
},
|
},
|
||||||
"WebHookUrlsV2": {
|
"WebHookUrlsV2": {
|
||||||
"description": "WebhookV2\n默认: ",
|
"description": "WebhookV2\n默认: ",
|
||||||
"markdownDescription": "WebhookV2 \n默认: ` `\n\n",
|
"markdownDescription": "WebhookV2 \n默认: ` `\n\n具体文档见 [Webhook](/docs/basic/webhook/)",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -56,7 +56,7 @@
|
||||||
},
|
},
|
||||||
"WpfShowTitleAndArea": {
|
"WpfShowTitleAndArea": {
|
||||||
"description": "在界面显示标题和分区\n默认: true",
|
"description": "在界面显示标题和分区\n默认: true",
|
||||||
"markdownDescription": "在界面显示标题和分区 \n默认: `true `\n\n",
|
"markdownDescription": "在界面显示标题和分区 \n默认: `true `\n\n只在桌面版(WPF版)有效",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -139,6 +139,24 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"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": {
|
"TimingStreamConnect": {
|
||||||
"description": "连接直播服务器超时时间 毫秒\n默认: 5000 (5秒)",
|
"description": "连接直播服务器超时时间 毫秒\n默认: 5000 (5秒)",
|
||||||
"markdownDescription": "连接直播服务器超时时间 毫秒 \n默认: `5000 (5秒) `\n\n",
|
"markdownDescription": "连接直播服务器超时时间 毫秒 \n默认: `5000 (5秒) `\n\n",
|
||||||
|
@ -213,7 +231,7 @@
|
||||||
},
|
},
|
||||||
"RecordMode": {
|
"RecordMode": {
|
||||||
"description": "录制模式\n默认: RecordMode.Standard",
|
"description": "录制模式\n默认: RecordMode.Standard",
|
||||||
"markdownDescription": "录制模式 \n默认: `RecordMode.Standard `\n\n",
|
"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",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -234,7 +252,7 @@
|
||||||
},
|
},
|
||||||
"CuttingMode": {
|
"CuttingMode": {
|
||||||
"description": "自动分段模式\n默认: CuttingMode.Disabled",
|
"description": "自动分段模式\n默认: CuttingMode.Disabled",
|
||||||
"markdownDescription": "自动分段模式 \n默认: `CuttingMode.Disabled `\n\n",
|
"markdownDescription": "自动分段模式 \n默认: `CuttingMode.Disabled `\n\n本设置项是一个 enum,键值对应如下:\n\n| 键 | 值 |\n|:--:|:--:|\n| CuttingMode.Disabled | 0 |\n| CuttingMode.ByTime | 1 |\n| CuttingMode.BySize | 2 |",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -256,7 +274,7 @@
|
||||||
},
|
},
|
||||||
"CuttingNumber": {
|
"CuttingNumber": {
|
||||||
"description": "自动分段数值\n默认: 100",
|
"description": "自动分段数值\n默认: 100",
|
||||||
"markdownDescription": "自动分段数值 \n默认: `100 `\n\n按时长分段时为分钟,按大小分段时为MiB",
|
"markdownDescription": "自动分段数值 \n默认: `100 `\n\n根据 CuttingMode 设置的不同: \n当按时长分段时,本设置的单位为分钟。 \n当按大小分段时,本设置的单位为MiB。",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -274,7 +292,7 @@
|
||||||
},
|
},
|
||||||
"RecordDanmaku": {
|
"RecordDanmaku": {
|
||||||
"description": "弹幕录制\n默认: false",
|
"description": "弹幕录制\n默认: false",
|
||||||
"markdownDescription": "弹幕录制 \n默认: `false `\n\n",
|
"markdownDescription": "弹幕录制 \n默认: `false `\n\n是否录制弹幕,`true` 为录制,`false` 为不录制。\n\n本设置同时是所有“弹幕录制”的总开关,当本设置为 `false` 时其他所有“弹幕录制”设置无效,不会写入弹幕XML文件。",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -290,7 +308,7 @@
|
||||||
},
|
},
|
||||||
"RecordDanmakuRaw": {
|
"RecordDanmakuRaw": {
|
||||||
"description": "弹幕录制-原始数据\n默认: false",
|
"description": "弹幕录制-原始数据\n默认: false",
|
||||||
"markdownDescription": "弹幕录制-原始数据 \n默认: `false `\n\n",
|
"markdownDescription": "弹幕录制-原始数据 \n默认: `false `\n\n是否记录原始 JSON 数据。\n\n弹幕原始数据会保存到 XML 文件每一条弹幕数据的 `raw` attribute 上。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -306,7 +324,7 @@
|
||||||
},
|
},
|
||||||
"RecordDanmakuSuperChat": {
|
"RecordDanmakuSuperChat": {
|
||||||
"description": "弹幕录制-SuperChat\n默认: true",
|
"description": "弹幕录制-SuperChat\n默认: true",
|
||||||
"markdownDescription": "弹幕录制-SuperChat \n默认: `true `\n\n",
|
"markdownDescription": "弹幕录制-SuperChat \n默认: `true `\n\n是否记录 SuperChat。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -322,7 +340,7 @@
|
||||||
},
|
},
|
||||||
"RecordDanmakuGift": {
|
"RecordDanmakuGift": {
|
||||||
"description": "弹幕录制-礼物\n默认: false",
|
"description": "弹幕录制-礼物\n默认: false",
|
||||||
"markdownDescription": "弹幕录制-礼物 \n默认: `false `\n\n",
|
"markdownDescription": "弹幕录制-礼物 \n默认: `false `\n\n是否记录礼物。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -338,7 +356,7 @@
|
||||||
},
|
},
|
||||||
"RecordDanmakuGuard": {
|
"RecordDanmakuGuard": {
|
||||||
"description": "弹幕录制-上船\n默认: true",
|
"description": "弹幕录制-上船\n默认: true",
|
||||||
"markdownDescription": "弹幕录制-上船 \n默认: `true `\n\n",
|
"markdownDescription": "弹幕录制-上船 \n默认: `true `\n\n是否记录上船(购买舰长)。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -354,7 +372,7 @@
|
||||||
},
|
},
|
||||||
"RecordingQuality": {
|
"RecordingQuality": {
|
||||||
"description": "直播画质\n默认: 10000",
|
"description": "直播画质\n默认: 10000",
|
||||||
"markdownDescription": "直播画质 \n默认: `10000 `\n\n录制的直播画质 qn 值,逗号分割,靠前的优先",
|
"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",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -393,7 +411,7 @@
|
||||||
},
|
},
|
||||||
"AutoRecord": {
|
"AutoRecord": {
|
||||||
"description": "自动录制\n默认: default",
|
"description": "自动录制\n默认: default",
|
||||||
"markdownDescription": "自动录制 \n默认: `default `\n\n",
|
"markdownDescription": "自动录制 \n默认: `default `\n\n设为 `true` 为启用自动录制,`false` 为不自动录制。",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -408,7 +426,7 @@
|
||||||
},
|
},
|
||||||
"RecordMode": {
|
"RecordMode": {
|
||||||
"description": "录制模式\n默认: RecordMode.Standard",
|
"description": "录制模式\n默认: RecordMode.Standard",
|
||||||
"markdownDescription": "录制模式 \n默认: `RecordMode.Standard `\n\n",
|
"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",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -429,7 +447,7 @@
|
||||||
},
|
},
|
||||||
"CuttingMode": {
|
"CuttingMode": {
|
||||||
"description": "自动分段模式\n默认: CuttingMode.Disabled",
|
"description": "自动分段模式\n默认: CuttingMode.Disabled",
|
||||||
"markdownDescription": "自动分段模式 \n默认: `CuttingMode.Disabled `\n\n",
|
"markdownDescription": "自动分段模式 \n默认: `CuttingMode.Disabled `\n\n本设置项是一个 enum,键值对应如下:\n\n| 键 | 值 |\n|:--:|:--:|\n| CuttingMode.Disabled | 0 |\n| CuttingMode.ByTime | 1 |\n| CuttingMode.BySize | 2 |",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -451,7 +469,7 @@
|
||||||
},
|
},
|
||||||
"CuttingNumber": {
|
"CuttingNumber": {
|
||||||
"description": "自动分段数值\n默认: 100",
|
"description": "自动分段数值\n默认: 100",
|
||||||
"markdownDescription": "自动分段数值 \n默认: `100 `\n\n按时长分段时为分钟,按大小分段时为MiB",
|
"markdownDescription": "自动分段数值 \n默认: `100 `\n\n根据 CuttingMode 设置的不同: \n当按时长分段时,本设置的单位为分钟。 \n当按大小分段时,本设置的单位为MiB。",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -469,7 +487,7 @@
|
||||||
},
|
},
|
||||||
"RecordDanmaku": {
|
"RecordDanmaku": {
|
||||||
"description": "弹幕录制\n默认: false",
|
"description": "弹幕录制\n默认: false",
|
||||||
"markdownDescription": "弹幕录制 \n默认: `false `\n\n",
|
"markdownDescription": "弹幕录制 \n默认: `false `\n\n是否录制弹幕,`true` 为录制,`false` 为不录制。\n\n本设置同时是所有“弹幕录制”的总开关,当本设置为 `false` 时其他所有“弹幕录制”设置无效,不会写入弹幕XML文件。",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -485,7 +503,7 @@
|
||||||
},
|
},
|
||||||
"RecordDanmakuRaw": {
|
"RecordDanmakuRaw": {
|
||||||
"description": "弹幕录制-原始数据\n默认: false",
|
"description": "弹幕录制-原始数据\n默认: false",
|
||||||
"markdownDescription": "弹幕录制-原始数据 \n默认: `false `\n\n",
|
"markdownDescription": "弹幕录制-原始数据 \n默认: `false `\n\n是否记录原始 JSON 数据。\n\n弹幕原始数据会保存到 XML 文件每一条弹幕数据的 `raw` attribute 上。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -501,7 +519,7 @@
|
||||||
},
|
},
|
||||||
"RecordDanmakuSuperChat": {
|
"RecordDanmakuSuperChat": {
|
||||||
"description": "弹幕录制-SuperChat\n默认: true",
|
"description": "弹幕录制-SuperChat\n默认: true",
|
||||||
"markdownDescription": "弹幕录制-SuperChat \n默认: `true `\n\n",
|
"markdownDescription": "弹幕录制-SuperChat \n默认: `true `\n\n是否记录 SuperChat。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -517,7 +535,7 @@
|
||||||
},
|
},
|
||||||
"RecordDanmakuGift": {
|
"RecordDanmakuGift": {
|
||||||
"description": "弹幕录制-礼物\n默认: false",
|
"description": "弹幕录制-礼物\n默认: false",
|
||||||
"markdownDescription": "弹幕录制-礼物 \n默认: `false `\n\n",
|
"markdownDescription": "弹幕录制-礼物 \n默认: `false `\n\n是否记录礼物。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -533,7 +551,7 @@
|
||||||
},
|
},
|
||||||
"RecordDanmakuGuard": {
|
"RecordDanmakuGuard": {
|
||||||
"description": "弹幕录制-上船\n默认: true",
|
"description": "弹幕录制-上船\n默认: true",
|
||||||
"markdownDescription": "弹幕录制-上船 \n默认: `true `\n\n",
|
"markdownDescription": "弹幕录制-上船 \n默认: `true `\n\n是否记录上船(购买舰长)。\n\n当 `RecordDanmaku` 为 `false` 时本项设置无效。",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -549,7 +567,7 @@
|
||||||
},
|
},
|
||||||
"RecordingQuality": {
|
"RecordingQuality": {
|
||||||
"description": "直播画质\n默认: 10000",
|
"description": "直播画质\n默认: 10000",
|
||||||
"markdownDescription": "直播画质 \n默认: `10000 `\n\n录制的直播画质 qn 值,逗号分割,靠前的优先",
|
"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",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
|
@ -165,6 +165,16 @@ export const data: Array<ConfigEntry> = [
|
||||||
defaultValueDescription: "6000 (6秒)",
|
defaultValueDescription: "6000 (6秒)",
|
||||||
markdown: ""
|
markdown: ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "TimingStreamRetryNoQn",
|
||||||
|
description: "录制无指定画质重连时间间隔 秒",
|
||||||
|
type: "uint",
|
||||||
|
configType: "globalOnly",
|
||||||
|
advancedConfig: true,
|
||||||
|
defaultValue: "90",
|
||||||
|
defaultValueDescription: "90 (1.5分钟)",
|
||||||
|
markdown: ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "TimingStreamConnect",
|
name: "TimingStreamConnect",
|
||||||
description: "连接直播服务器超时时间 毫秒",
|
description: "连接直播服务器超时时间 毫秒",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user