mirror of
https://github.com/BililiveRecorder/BililiveRecorder.git
synced 2024-11-16 11:42:22 +08:00
支持录制画质选择
This commit is contained in:
parent
f0f45bab39
commit
2b482a9c4b
|
@ -110,12 +110,12 @@ namespace BililiveRecorder.Core.Api.Http
|
|||
return FetchAsync<UserInfo>(this.mainClient, url);
|
||||
}
|
||||
|
||||
public Task<BilibiliApiResponse<RoomPlayInfo>> GetStreamUrlAsync(int roomid)
|
||||
public Task<BilibiliApiResponse<RoomPlayInfo>> GetStreamUrlAsync(int roomid, int qn)
|
||||
{
|
||||
if (this.disposedValue)
|
||||
throw new ObjectDisposedException(nameof(HttpApiClient));
|
||||
|
||||
var url = $@"{this.config.LiveApiHost}/xlive/web-room/v2/index/getRoomPlayInfo?room_id={roomid}&protocol=0%2C1&format=0%2C2&codec=0%2C1&qn=10000&platform=web&ptype=16";
|
||||
var url = $@"{this.config.LiveApiHost}/xlive/web-room/v2/index/getRoomPlayInfo?room_id={roomid}&protocol=0,1&format=0,1,2&codec=0,1&qn={qn}&platform=web&ptype=8";
|
||||
return FetchAsync<RoomPlayInfo>(this.mainClient, url);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,6 @@ namespace BililiveRecorder.Core.Api
|
|||
{
|
||||
Task<BilibiliApiResponse<RoomInfo>> GetRoomInfoAsync(int roomid);
|
||||
Task<BilibiliApiResponse<UserInfo>> GetUserInfoAsync(int roomid);
|
||||
Task<BilibiliApiResponse<RoomPlayInfo>> GetStreamUrlAsync(int roomid);
|
||||
Task<BilibiliApiResponse<RoomPlayInfo>> GetStreamUrlAsync(int roomid, int qn);
|
||||
}
|
||||
}
|
||||
|
|
26
BililiveRecorder.Core/Api/IApiClientExtensions.cs
Normal file
26
BililiveRecorder.Core/Api/IApiClientExtensions.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using static BililiveRecorder.Core.Api.Model.RoomPlayInfo;
|
||||
|
||||
namespace BililiveRecorder.Core.Api
|
||||
{
|
||||
public static class IApiClientExtensions
|
||||
{
|
||||
public static async Task<CodecItem?> GetCodecItemInStreamUrlAsync(this IApiClient apiClient, int roomid, int qn)
|
||||
{
|
||||
var apiResp = await apiClient.GetStreamUrlAsync(roomid: roomid, qn: qn).ConfigureAwait(false);
|
||||
var url_data = apiResp?.Data?.PlayurlInfo?.Playurl?.Streams;
|
||||
|
||||
if (url_data is null)
|
||||
throw new Exception("playurl is null");
|
||||
|
||||
var url_http_stream_flv_avc =
|
||||
url_data.FirstOrDefault(x => x.ProtocolName == "http_stream")
|
||||
?.Formats?.FirstOrDefault(x => x.FormatName == "flv")
|
||||
?.Codecs?.FirstOrDefault(x => x.CodecName == "avc");
|
||||
|
||||
return url_http_stream_flv_avc;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,9 +27,9 @@ namespace BililiveRecorder.Core.Api
|
|||
.ExecuteAsync(_ => this.client.GetRoomInfoAsync(roomid), new Context(PolicyNames.CacheKeyRoomInfo + ":" + roomid))
|
||||
.ConfigureAwait(false);
|
||||
|
||||
public async Task<BilibiliApiResponse<RoomPlayInfo>> GetStreamUrlAsync(int roomid) => await this.policies
|
||||
public async Task<BilibiliApiResponse<RoomPlayInfo>> GetStreamUrlAsync(int roomid, int qn) => await this.policies
|
||||
.Get<IAsyncPolicy>(PolicyNames.PolicyStreamApiRequestAsync)
|
||||
.ExecuteAsync(_ => this.client.GetStreamUrlAsync(roomid), new Context(PolicyNames.CacheKeyStream + ":" + roomid))
|
||||
.ExecuteAsync(_ => this.client.GetStreamUrlAsync(roomid, qn), new Context(PolicyNames.CacheKeyStream + ":" + roomid + ":" + qn))
|
||||
.ConfigureAwait(false);
|
||||
|
||||
public async Task<BilibiliApiResponse<UserInfo>> GetUserInfoAsync(int roomid) => await this.policies
|
||||
|
|
|
@ -92,6 +92,14 @@ namespace BililiveRecorder.Core.Config.V2
|
|||
[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>
|
||||
/// 录制的直播画质 qn 值,逗号分割,靠前的优先
|
||||
/// </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>
|
||||
|
@ -317,6 +325,14 @@ namespace BililiveRecorder.Core.Config.V2
|
|||
[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>
|
||||
/// 录制的直播画质 qn 值,逗号分割,靠前的优先
|
||||
/// </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)); }
|
||||
|
||||
}
|
||||
|
||||
public sealed partial class DefaultConfig
|
||||
|
@ -364,6 +380,8 @@ namespace BililiveRecorder.Core.Config.V2
|
|||
|
||||
public bool RecordDanmakuGuard => true;
|
||||
|
||||
public string RecordingQuality => "10000";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ module.exports = {
|
|||
"type": "bool",
|
||||
"desc": "是否显示直播间标题和分区",
|
||||
"default": "true",
|
||||
}, ],
|
||||
},],
|
||||
"room": [{
|
||||
"name": "RoomId",
|
||||
"type": "int",
|
||||
|
@ -117,5 +117,10 @@ module.exports = {
|
|||
"type": "bool",
|
||||
"desc": "是否同时录制 上船",
|
||||
"default": "true"
|
||||
}, ]
|
||||
}, {
|
||||
"name": "RecordingQuality",
|
||||
"type": "string",
|
||||
"desc": "录制的直播画质 qn 值,逗号分割,靠前的优先",
|
||||
"default": "\"10000\""
|
||||
},]
|
||||
}
|
||||
|
|
|
@ -139,6 +139,21 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"RecordingQuality": {
|
||||
"description": "录制的直播画质 qn 值,逗号分割,靠前的优先",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "string",
|
||||
"default": "10000"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TimingStreamRetry": {
|
||||
"description": "录制断开重连时间间隔 毫秒",
|
||||
"type": "object",
|
||||
|
@ -471,6 +486,21 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"RecordingQuality": {
|
||||
"description": "录制的直播画质 qn 值,逗号分割,靠前的优先",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"HasValue": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"Value": {
|
||||
"type": "string",
|
||||
"default": "10000"
|
||||
}
|
||||
}
|
||||
},
|
||||
"RoomId": {
|
||||
"description": "房间号",
|
||||
"type": "object",
|
||||
|
|
|
@ -236,24 +236,56 @@ namespace BililiveRecorder.Core.Recording
|
|||
|
||||
protected async Task<string> FetchStreamUrlAsync(int roomid)
|
||||
{
|
||||
var apiResp = await this.apiClient.GetStreamUrlAsync(roomid: roomid).ConfigureAwait(false);
|
||||
var url_data = apiResp?.Data?.PlayurlInfo?.Playurl?.Streams;
|
||||
const int DefaultQn = 10000;
|
||||
var selected_qn = DefaultQn;
|
||||
int[] qns;
|
||||
Api.Model.RoomPlayInfo.UrlInfoItem[]? url_infos;
|
||||
|
||||
if (url_data is null)
|
||||
throw new Exception("playurl is null");
|
||||
var codecItem = await this.apiClient.GetCodecItemInStreamUrlAsync(roomid: roomid, qn: DefaultQn).ConfigureAwait(false);
|
||||
|
||||
var url_http_stream_flv_avc =
|
||||
url_data.FirstOrDefault(x => x.ProtocolName == "http_stream")
|
||||
?.Formats?.FirstOrDefault(x => x.FormatName == "flv")
|
||||
?.Codecs?.FirstOrDefault(x => x.CodecName == "avc");
|
||||
if (codecItem is null)
|
||||
throw new Exception("no supported stream url, qn: " + DefaultQn);
|
||||
|
||||
if (url_http_stream_flv_avc is null)
|
||||
throw new Exception("no supported stream url");
|
||||
{
|
||||
try
|
||||
{
|
||||
qns = this.room.RoomConfig.RecordingQuality.Split(new[] { ',', ',', '、', ' ' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => int.TryParse(x, out var num) ? num : -1)
|
||||
.Where(x => x > 0)
|
||||
.ToArray();
|
||||
|
||||
if (url_http_stream_flv_avc.CurrentQn != 10000)
|
||||
this.logger.Warning("当前录制的画质是 {CurrentQn}", url_http_stream_flv_avc.CurrentQn);
|
||||
foreach (var qn in qns)
|
||||
{
|
||||
if (codecItem.AcceptQn.Contains(qn))
|
||||
{
|
||||
selected_qn = qn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var url_infos = url_http_stream_flv_avc.UrlInfos;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (selected_qn != DefaultQn)
|
||||
{
|
||||
// 最终选择的 qn 与默认不同,需要重新请求一次
|
||||
codecItem = await this.apiClient.GetCodecItemInStreamUrlAsync(roomid: roomid, qn: selected_qn).ConfigureAwait(false);
|
||||
|
||||
if (codecItem is null)
|
||||
throw new Exception("no supported stream url, qn: " + DefaultQn);
|
||||
}
|
||||
|
||||
if (codecItem.CurrentQn != selected_qn || !qns.Contains(codecItem.CurrentQn))
|
||||
this.logger.Warning("当前录制的画质是 {CurrentQn}", codecItem.CurrentQn);
|
||||
|
||||
url_infos = codecItem.UrlInfos;
|
||||
if (url_infos is null || url_infos.Length == 0)
|
||||
throw new Exception("no url_info");
|
||||
|
||||
|
@ -264,7 +296,7 @@ namespace BililiveRecorder.Core.Recording
|
|||
? url_infos_without_mcdn[this.random.Next(url_infos_without_mcdn.Length)]
|
||||
: url_infos[this.random.Next(url_infos.Length)];
|
||||
|
||||
var fullUrl = url_info.Host + url_http_stream_flv_avc.BaseUrl + url_info.Extra;
|
||||
var fullUrl = url_info.Host + codecItem.BaseUrl + url_info.Extra;
|
||||
return fullUrl;
|
||||
}
|
||||
|
||||
|
|
|
@ -88,6 +88,15 @@
|
|||
</local:SettingWithDefault>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
<GroupBox Header="录制画质">
|
||||
<StackPanel>
|
||||
<TextBlock Text="逗号分割的录制画质ID"/>
|
||||
<local:SettingWithDefault IsSettingNotUsingDefault="{Binding HasRecordingQuality}">
|
||||
<TextBox Text="{Binding RecordingQuality,UpdateSourceTrigger=PropertyChanged,Delay=1000}"
|
||||
ui:TextBoxHelper.IsDeleteButtonVisible="False" Width="220" HorizontalAlignment="Left"/>
|
||||
</local:SettingWithDefault>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
</ui:SimpleStackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
|
|
@ -99,6 +99,15 @@
|
|||
Text="{Binding WebHookUrls,UpdateSourceTrigger=PropertyChanged,Delay=1000}" ui:TextBoxHelper.IsDeleteButtonVisible="False"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
<GroupBox Header="录制画质">
|
||||
<StackPanel MaxWidth="400" HorizontalAlignment="Left">
|
||||
<TextBlock Text="逗号分割的录制画质ID"/>
|
||||
<c:SettingWithDefault IsSettingNotUsingDefault="{Binding HasRecordingQuality}">
|
||||
<TextBox Text="{Binding RecordingQuality,UpdateSourceTrigger=PropertyChanged,Delay=1000}"
|
||||
ui:TextBoxHelper.IsDeleteButtonVisible="False" Width="220" HorizontalAlignment="Left"/>
|
||||
</c:SettingWithDefault>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
</ui:SimpleStackPanel>
|
||||
</ScrollViewer>
|
||||
</ui:Page>
|
||||
|
|
Loading…
Reference in New Issue
Block a user