From 2a38e2ccb91be228461b366320270fad1f38a391 Mon Sep 17 00:00:00 2001 From: genteure Date: Sat, 19 Aug 2023 19:00:48 +0800 Subject: [PATCH] fix(core): fix websocket danmu transport --- .../Api/Danmaku/DanmakuTransportWebSocket.cs | 43 ++++++++++++++----- .../Api/Http/HttpApiClient.cs | 8 ++-- BililiveRecorder.Core/Room.cs | 7 ++- 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/BililiveRecorder.Core/Api/Danmaku/DanmakuTransportWebSocket.cs b/BililiveRecorder.Core/Api/Danmaku/DanmakuTransportWebSocket.cs index 1da462e..1fd9546 100644 --- a/BililiveRecorder.Core/Api/Danmaku/DanmakuTransportWebSocket.cs +++ b/BililiveRecorder.Core/Api/Danmaku/DanmakuTransportWebSocket.cs @@ -1,5 +1,7 @@ using System; +using System.Collections; using System.IO.Pipelines; +using System.Net; using System.Net.WebSockets; using System.Runtime.InteropServices; using System.Threading; @@ -11,24 +13,43 @@ namespace BililiveRecorder.Core.Api.Danmaku { internal class DanmakuTransportWebSocket : IDanmakuTransport { - private static readonly bool isDotNetFramework = RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework", StringComparison.Ordinal); - private readonly ClientWebSocket socket; protected virtual string Scheme => "ws"; + static DanmakuTransportWebSocket() + { + if (!RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework", StringComparison.Ordinal)) + return; + + var headerInfoTable = typeof(WebHeaderCollection).Assembly.GetType("System.Net.HeaderInfoTable", false); + if (headerInfoTable is null) return; + + var headerHashTable = headerInfoTable.GetField("HeaderHashTable", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); + if (headerHashTable is null) return; + + if (headerHashTable.GetValue(null) is not Hashtable table) return; + + var info = table["User-Agent"]; + if (info is null) return; + + var isRequestRestrictedProperty = info.GetType().GetField("IsRequestRestricted", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); + if (isRequestRestrictedProperty is null) return; + + isRequestRestrictedProperty.SetValue(info, false); + } + public DanmakuTransportWebSocket() { this.socket = new ClientWebSocket(); - this.socket.Options.UseDefaultCredentials = false; - this.socket.Options.Credentials = null; - this.socket.Options.Proxy = null; - this.socket.Options.Cookies = null; - - this.socket.Options.SetRequestHeader("Origin", HttpApiClient.HttpHeaderOrigin); - - if (!isDotNetFramework) - this.socket.Options.SetRequestHeader("User-Agent", HttpApiClient.HttpHeaderUserAgent); + var options = this.socket.Options; + options.UseDefaultCredentials = false; + options.Credentials = null; + options.Proxy = null; + options.Cookies = null; + options.SetRequestHeader("Origin", HttpApiClient.HttpHeaderOrigin); + options.SetRequestHeader("Accept-Language", HttpApiClient.HttpHeaderAcceptLanguage); + options.SetRequestHeader("User-Agent", HttpApiClient.HttpHeaderUserAgent); } public async Task ConnectAsync(string host, int port, CancellationToken cancellationToken) diff --git a/BililiveRecorder.Core/Api/Http/HttpApiClient.cs b/BililiveRecorder.Core/Api/Http/HttpApiClient.cs index f0a3c6b..3997901 100644 --- a/BililiveRecorder.Core/Api/Http/HttpApiClient.cs +++ b/BililiveRecorder.Core/Api/Http/HttpApiClient.cs @@ -15,9 +15,10 @@ namespace BililiveRecorder.Core.Api.Http internal class HttpApiClient : IApiClient, IDanmakuServerApiClient, ICookieTester { internal const string HttpHeaderAccept = "application/json, text/javascript, */*; q=0.01"; + internal const string HttpHeaderAcceptLanguage = "zh-CN"; internal const string HttpHeaderReferer = "https://live.bilibili.com/"; internal const string HttpHeaderOrigin = "https://live.bilibili.com"; - internal const string HttpHeaderUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"; + internal const string HttpHeaderUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"; private static readonly Regex matchCookieUidRegex = new Regex(@"DedeUserID=(\d+?);", RegexOptions.Compiled); private static readonly Regex matchCookieBuvid3Regex = new Regex(@"buvid3=(.+?);", RegexOptions.Compiled); private long uid; @@ -49,6 +50,7 @@ namespace BililiveRecorder.Core.Api.Http }; var headers = client.DefaultRequestHeaders; headers.Add("Accept", HttpHeaderAccept); + headers.Add("Accept-Language", HttpHeaderAcceptLanguage); headers.Add("Origin", HttpHeaderOrigin); headers.Add("Referer", HttpHeaderReferer); headers.Add("User-Agent", HttpHeaderUserAgent); @@ -59,7 +61,7 @@ namespace BililiveRecorder.Core.Api.Http headers.Add("Cookie", cookie_string); long.TryParse(matchCookieUidRegex.Match(cookie_string).Groups[1].Value, out var uid); this.uid = uid; - string buvid3 = matchCookieBuvid3Regex.Match(cookie_string).Groups[1].Value; + var buvid3 = matchCookieBuvid3Regex.Match(cookie_string).Groups[1].Value; if (!string.IsNullOrWhiteSpace(buvid3)) this.buvid3 = buvid3; else @@ -137,7 +139,7 @@ namespace BililiveRecorder.Core.Api.Http if (jo["code"]?.ToObject() != 0) return (false, $"Response:\n{resp}"); - string message = $@"User: {jo["data"]?["uname"]?.ToObject()} + var message = $@"User: {jo["data"]?["uname"]?.ToObject()} UID (from API response): {jo["data"]?["uid"]?.ToObject()} UID (from Cookie): {this.GetUid()} BUVID3 (from Cookie): {this.GetBuvid3()}"; diff --git a/BililiveRecorder.Core/Room.cs b/BililiveRecorder.Core/Room.cs index 8ab6e5a..2f18822 100644 --- a/BililiveRecorder.Core/Room.cs +++ b/BililiveRecorder.Core/Room.cs @@ -209,6 +209,8 @@ namespace BililiveRecorder.Core var room = (await this.apiClient.GetRoomInfoAsync(this.RoomConfig.RoomId).ConfigureAwait(false)).Data; if (room != null) { + this.logger.Debug("拉取房间信息成功: {@room}", room); + this.RoomConfig.RoomId = room.Room.RoomId; this.ShortId = room.Room.ShortId; this.Uid = room.Room.Uid; @@ -479,7 +481,7 @@ namespace BililiveRecorder.Core { const int MAX_ATTEMPT = 3; var attempt = 0; -retry: + retry: try { var coverUrl = this.RawBilibiliApiJsonData?["room_info"]?["cover"]?.ToObject(); @@ -575,12 +577,15 @@ retry: switch (d.MsgType) { case Api.Danmaku.DanmakuMsgType.LiveStart: + this.logger.Debug("推送直播开始"); this.Streaming = true; break; case Api.Danmaku.DanmakuMsgType.LiveEnd: + this.logger.Debug("推送直播结束"); this.Streaming = false; break; case Api.Danmaku.DanmakuMsgType.RoomChange: + this.logger.Debug("推送房间信息变更, {Title}, {AreaNameParent}, {AreaNameChild}", d.Title, d.ParentAreaName, d.AreaName); this.Title = d.Title ?? this.Title; this.AreaNameParent = d.ParentAreaName ?? this.AreaNameParent; this.AreaNameChild = d.AreaName ?? this.AreaNameChild;