Core: Add config options to ignore system proxy, use IPv4 and/or IPv6

This commit is contained in:
genteure 2022-05-09 22:42:08 +08:00
parent 2fe47fd6f1
commit 43d1c6f2ef
9 changed files with 161 additions and 12 deletions

View File

@ -35,7 +35,9 @@ namespace BililiveRecorder.Cli.Configure
TimingStreamConnect,
TimingDanmakuRetry,
TimingWatchdogTimeout,
RecordDanmakuFlushInterval
RecordDanmakuFlushInterval,
NetworkTransportUseSystemProxy,
NetworkTransportAllowedAddressFamily
}
public enum RoomConfigProperties
{
@ -82,6 +84,8 @@ namespace BililiveRecorder.Cli.Configure
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.RecordDanmakuFlushInterval, new ConfigInstruction<GlobalConfig, uint>(config => config.HasRecordDanmakuFlushInterval = false, (config, value) => config.RecordDanmakuFlushInterval = value) { Name = "RecordDanmakuFlushInterval", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.NetworkTransportUseSystemProxy, new ConfigInstruction<GlobalConfig, bool>(config => config.HasNetworkTransportUseSystemProxy = false, (config, value) => config.NetworkTransportUseSystemProxy = value) { Name = "NetworkTransportUseSystemProxy", CanBeOptional = true });
GlobalConfig.Add(GlobalConfigProperties.NetworkTransportAllowedAddressFamily, new ConfigInstruction<GlobalConfig, AllowedAddressFamily>(config => config.HasNetworkTransportAllowedAddressFamily = false, (config, value) => config.NetworkTransportAllowedAddressFamily = value) { Name = "NetworkTransportAllowedAddressFamily", CanBeOptional = true });
RoomConfig.Add(RoomConfigProperties.RoomId, new ConfigInstruction<RoomConfig, int>(config => config.HasRoomId = false, (config, value) => config.RoomId = value) { Name = "RoomId", CanBeOptional = false });
RoomConfig.Add(RoomConfigProperties.AutoRecord, new ConfigInstruction<RoomConfig, bool>(config => config.HasAutoRecord = false, (config, value) => config.AutoRecord = value) { Name = "AutoRecord", CanBeOptional = false });

View File

@ -4,9 +4,10 @@ using System.CommandLine;
using System.CommandLine.Invocation;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using BililiveRecorder.Cli.Configure;
using System.Threading.Tasks;
using BililiveRecorder.Cli.Configure;
using BililiveRecorder.Core;
using BililiveRecorder.Core.Config;
using BililiveRecorder.Core.Config.V3;
@ -217,6 +218,7 @@ namespace BililiveRecorder.Cli
.Enrich.WithThreadName()
.Enrich.FromLogContext()
.Enrich.WithExceptionDetails()
.Destructure.AsScalar<IPAddress>()
.Destructure.ByTransforming<Flv.Xml.XmlFlvFile.XmlFlvFileMeta>(x => new
{
x.Version,

View File

@ -0,0 +1,10 @@
namespace BililiveRecorder.Core.Config
{
public enum AllowedAddressFamily
{
System = -1,
Any = 0,
Ipv4 = 1,
Ipv6 = 2,
}
}

View File

@ -166,6 +166,16 @@ namespace BililiveRecorder.Core.Config.V3
/// </summary>
public uint RecordDanmakuFlushInterval => this.GetPropertyValue<uint>();
/// <summary>
/// 是否使用系统代理
/// </summary>
public bool NetworkTransportUseSystemProxy => this.GetPropertyValue<bool>();
/// <summary>
/// 允许使用的 IP 网络类型
/// </summary>
public AllowedAddressFamily NetworkTransportAllowedAddressFamily => this.GetPropertyValue<AllowedAddressFamily>();
}
[JsonObject(MemberSerialization.OptIn)]
@ -347,6 +357,22 @@ namespace BililiveRecorder.Core.Config.V3
[JsonProperty(nameof(RecordDanmakuFlushInterval)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<uint> OptionalRecordDanmakuFlushInterval { get => this.GetPropertyValueOptional<uint>(nameof(this.RecordDanmakuFlushInterval)); set => this.SetPropertyValueOptional(value, nameof(this.RecordDanmakuFlushInterval)); }
/// <summary>
/// 是否使用系统代理
/// </summary>
public bool NetworkTransportUseSystemProxy { get => this.GetPropertyValue<bool>(); set => this.SetPropertyValue(value); }
public bool HasNetworkTransportUseSystemProxy { get => this.GetPropertyHasValue(nameof(this.NetworkTransportUseSystemProxy)); set => this.SetPropertyHasValue<bool>(value, nameof(this.NetworkTransportUseSystemProxy)); }
[JsonProperty(nameof(NetworkTransportUseSystemProxy)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<bool> OptionalNetworkTransportUseSystemProxy { get => this.GetPropertyValueOptional<bool>(nameof(this.NetworkTransportUseSystemProxy)); set => this.SetPropertyValueOptional(value, nameof(this.NetworkTransportUseSystemProxy)); }
/// <summary>
/// 允许使用的 IP 网络类型
/// </summary>
public AllowedAddressFamily NetworkTransportAllowedAddressFamily { get => this.GetPropertyValue<AllowedAddressFamily>(); set => this.SetPropertyValue(value); }
public bool HasNetworkTransportAllowedAddressFamily { get => this.GetPropertyHasValue(nameof(this.NetworkTransportAllowedAddressFamily)); set => this.SetPropertyHasValue<AllowedAddressFamily>(value, nameof(this.NetworkTransportAllowedAddressFamily)); }
[JsonProperty(nameof(NetworkTransportAllowedAddressFamily)), EditorBrowsable(EditorBrowsableState.Never)]
public Optional<AllowedAddressFamily> OptionalNetworkTransportAllowedAddressFamily { get => this.GetPropertyValueOptional<AllowedAddressFamily>(nameof(this.NetworkTransportAllowedAddressFamily)); set => this.SetPropertyValueOptional(value, nameof(this.NetworkTransportAllowedAddressFamily)); }
}
public sealed partial class DefaultConfig
@ -398,6 +424,10 @@ namespace BililiveRecorder.Core.Config.V3
public uint RecordDanmakuFlushInterval => 20;
public bool NetworkTransportUseSystemProxy => false;
public AllowedAddressFamily NetworkTransportAllowedAddressFamily => AllowedAddressFamily.Any;
}
}

View File

@ -2,11 +2,13 @@ using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using BililiveRecorder.Core.Api;
using BililiveRecorder.Core.Config;
using BililiveRecorder.Core.Event;
using BililiveRecorder.Core.Templating;
using Serilog;
@ -19,7 +21,7 @@ namespace BililiveRecorder.Core.Recording
private const string HttpHeaderAccept = "*/*";
private const string HttpHeaderOrigin = "https://live.bilibili.com";
private const string HttpHeaderReferer = "https://live.bilibili.com/";
private const string HttpHeaderUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36";
private const string HttpHeaderUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36";
private const int timer_inverval = 2;
protected readonly Timer timer = new Timer(1000 * timer_inverval);
@ -199,11 +201,12 @@ namespace BililiveRecorder.Core.Recording
#region Api Requests
private static HttpClient CreateHttpClient()
private HttpClient CreateHttpClient()
{
var httpClient = new HttpClient(new HttpClientHandler
{
AllowAutoRedirect = false
AllowAutoRedirect = false,
UseProxy = this.room.RoomConfig.NetworkTransportUseSystemProxy,
});
var headers = httpClient.DefaultRequestHeaders;
headers.Add("Accept", HttpHeaderAccept);
@ -273,25 +276,64 @@ namespace BililiveRecorder.Core.Recording
protected async Task<Stream> GetStreamAsync(string fullUrl, int timeout)
{
var client = CreateHttpClient();
var client = this.CreateHttpClient();
while (true)
{
var resp = await client.GetAsync(fullUrl,
var originalUri = new Uri(fullUrl);
var allowedAddressFamily = this.room.RoomConfig.NetworkTransportAllowedAddressFamily;
HttpRequestMessage request;
if (allowedAddressFamily == AllowedAddressFamily.System)
{
this.logger.Debug("NetworkTransportAllowedAddressFamily is System");
request = new HttpRequestMessage(HttpMethod.Get, originalUri);
}
else
{
var ips = await Dns.GetHostAddressesAsync(originalUri.DnsSafeHost);
var filtered = ips.Where(x => allowedAddressFamily switch
{
AllowedAddressFamily.Ipv4 => x.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork,
AllowedAddressFamily.Ipv6 => x.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6,
AllowedAddressFamily.Any => true,
_ => false
}).ToArray();
var selected = filtered[this.random.Next(filtered.Length)];
this.logger.Debug("指定直播服务器地址 {DnsHost}: {SelectedIp}, Allowed: {AllowedAddressFamily}, {IPAddresses}", originalUri.DnsSafeHost, selected, allowedAddressFamily, ips);
if (selected is null)
{
throw new Exception("DNS 没有返回符合要求的 IP 地址");
}
var builder = new UriBuilder(originalUri)
{
Host = selected.ToString()
};
request = new HttpRequestMessage(HttpMethod.Get, builder.Uri);
request.Headers.Host = originalUri.IsDefaultPort ? originalUri.Host : originalUri.Host + ":" + originalUri.Port;
}
var resp = await client.SendAsync(request,
HttpCompletionOption.ResponseHeadersRead,
new CancellationTokenSource(timeout).Token)
.ConfigureAwait(false);
switch (resp.StatusCode)
{
case System.Net.HttpStatusCode.OK:
case HttpStatusCode.OK:
{
this.logger.Information("开始接收直播流");
var stream = await resp.Content.ReadAsStreamAsync().ConfigureAwait(false);
return stream;
}
case System.Net.HttpStatusCode.Moved:
case System.Net.HttpStatusCode.Redirect:
case HttpStatusCode.Moved:
case HttpStatusCode.Redirect:
{
fullUrl = resp.Headers.Location.OriginalString;
this.logger.Debug("跳转到 {Url}", fullUrl);
@ -303,7 +345,6 @@ namespace BililiveRecorder.Core.Recording
}
}
}
#endregion
}
}

View File

@ -213,6 +213,7 @@ namespace BililiveRecorder.WPF
.Enrich.WithThreadName()
.Enrich.FromLogContext()
.Enrich.WithExceptionDetails()
.Destructure.AsScalar<IPAddress>()
.Destructure.ByTransforming<Flv.Xml.XmlFlvFile.XmlFlvFileMeta>(x => new
{
x.Version,
@ -221,7 +222,6 @@ namespace BililiveRecorder.WPF
x.FileCreationTime,
x.FileModificationTime,
})
.Destructure.AsScalar<IPAddress>()
.WriteTo.Console(levelSwitch: levelSwitchConsole)
#if DEBUG
.WriteTo.Debug()

View File

@ -62,6 +62,8 @@ namespace BililiveRecorder.Web.Models
public Optional<uint>? OptionalTimingDanmakuRetry { get; set; }
public Optional<uint>? OptionalTimingWatchdogTimeout { get; set; }
public Optional<uint>? OptionalRecordDanmakuFlushInterval { get; set; }
public Optional<bool>? OptionalNetworkTransportUseSystemProxy { get; set; }
public Optional<AllowedAddressFamily>? OptionalNetworkTransportAllowedAddressFamily { get; set; }
public void ApplyTo(GlobalConfig config)
{
@ -87,6 +89,8 @@ namespace BililiveRecorder.Web.Models
if (this.OptionalTimingDanmakuRetry.HasValue) config.OptionalTimingDanmakuRetry = this.OptionalTimingDanmakuRetry.Value;
if (this.OptionalTimingWatchdogTimeout.HasValue) config.OptionalTimingWatchdogTimeout = this.OptionalTimingWatchdogTimeout.Value;
if (this.OptionalRecordDanmakuFlushInterval.HasValue) config.OptionalRecordDanmakuFlushInterval = this.OptionalRecordDanmakuFlushInterval.Value;
if (this.OptionalNetworkTransportUseSystemProxy.HasValue) config.OptionalNetworkTransportUseSystemProxy = this.OptionalNetworkTransportUseSystemProxy.Value;
if (this.OptionalNetworkTransportAllowedAddressFamily.HasValue) config.OptionalNetworkTransportAllowedAddressFamily = this.OptionalNetworkTransportAllowedAddressFamily.Value;
}
}
@ -132,6 +136,8 @@ namespace BililiveRecorder.Web.Models.Rest
public Optional<uint> OptionalTimingDanmakuRetry { get; set; }
public Optional<uint> OptionalTimingWatchdogTimeout { get; set; }
public Optional<uint> OptionalRecordDanmakuFlushInterval { get; set; }
public Optional<bool> OptionalNetworkTransportUseSystemProxy { get; set; }
public Optional<AllowedAddressFamily> OptionalNetworkTransportAllowedAddressFamily { get; set; }
}
}
@ -182,6 +188,8 @@ namespace BililiveRecorder.Web.Models.Graphql
this.Field(x => x.OptionalTimingDanmakuRetry, type: typeof(HierarchicalOptionalType<uint>));
this.Field(x => x.OptionalTimingWatchdogTimeout, type: typeof(HierarchicalOptionalType<uint>));
this.Field(x => x.OptionalRecordDanmakuFlushInterval, type: typeof(HierarchicalOptionalType<uint>));
this.Field(x => x.OptionalNetworkTransportUseSystemProxy, type: typeof(HierarchicalOptionalType<bool>));
this.Field(x => x.OptionalNetworkTransportAllowedAddressFamily, type: typeof(HierarchicalOptionalType<AllowedAddressFamily>));
}
}
@ -211,6 +219,8 @@ namespace BililiveRecorder.Web.Models.Graphql
this.Field(x => x.TimingDanmakuRetry);
this.Field(x => x.TimingWatchdogTimeout);
this.Field(x => x.RecordDanmakuFlushInterval);
this.Field(x => x.NetworkTransportUseSystemProxy);
this.Field(x => x.NetworkTransportAllowedAddressFamily);
}
}
@ -257,6 +267,8 @@ namespace BililiveRecorder.Web.Models.Graphql
this.Field(x => x.OptionalTimingDanmakuRetry, nullable: true, type: typeof(HierarchicalOptionalInputType<uint>));
this.Field(x => x.OptionalTimingWatchdogTimeout, nullable: true, type: typeof(HierarchicalOptionalInputType<uint>));
this.Field(x => x.OptionalRecordDanmakuFlushInterval, nullable: true, type: typeof(HierarchicalOptionalInputType<uint>));
this.Field(x => x.OptionalNetworkTransportUseSystemProxy, nullable: true, type: typeof(HierarchicalOptionalInputType<bool>));
this.Field(x => x.OptionalNetworkTransportAllowedAddressFamily, nullable: true, type: typeof(HierarchicalOptionalInputType<AllowedAddressFamily>));
}
}

View File

@ -229,6 +229,38 @@
}
}
},
"NetworkTransportUseSystemProxy": {
"description": "是否使用系统代理\n默认: false",
"markdownDescription": "是否使用系统代理 \n默认: `false `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
"HasValue": {
"type": "boolean",
"default": true
},
"Value": {
"type": "boolean",
"default": false
}
}
},
"NetworkTransportAllowedAddressFamily": {
"description": "允许使用的 IP 网络类型\n默认: AllowedAddressFamily.Any",
"markdownDescription": "允许使用的 IP 网络类型 \n默认: `AllowedAddressFamily.Any `\n\n",
"type": "object",
"additionalProperties": false,
"properties": {
"HasValue": {
"type": "boolean",
"default": true
},
"Value": {
"type": "AllowedAddressFamily",
"default": "AllowedAddressFamily.Any"
}
}
},
"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/)",

View File

@ -216,4 +216,22 @@ export const data: Array<ConfigEntry> = [
xmlComment: "触发 <see cref=\"System.Xml.XmlWriter.Flush\"/> 的弹幕个数",
markdown: ""
},
{
name: "NetworkTransportUseSystemProxy",
description: "是否使用系统代理",
type: "bool",
defaultValue: "false",
configType: "globalOnly",
advancedConfig: true,
markdown: ""
},
{
name: "NetworkTransportAllowedAddressFamily",
description: "允许使用的 IP 网络类型",
type: "AllowedAddressFamily",
defaultValue: "AllowedAddressFamily.Any",
configType: "globalOnly",
advancedConfig: true,
markdown: ""
},
];