diff --git a/BBDown.Core/APP/Response/dmviewreply.proto b/BBDown.Core/APP/Response/dmviewreply.proto index eb6e73940..35362bd22 100644 --- a/BBDown.Core/APP/Response/dmviewreply.proto +++ b/BBDown.Core/APP/Response/dmviewreply.proto @@ -26,7 +26,7 @@ message VideoMask { message VideoSubtitle { optional string lan = 1; optional string lanDoc = 2; - optional SubtitleItem subtitles = 3; + repeated SubtitleItem subtitles = 3; } diff --git a/BBDown.Core/AppHelper.cs b/BBDown.Core/AppHelper.cs index 35a310947..9b84704f4 100644 --- a/BBDown.Core/AppHelper.cs +++ b/BBDown.Core/AppHelper.cs @@ -3,9 +3,9 @@ using System.Buffers.Binary; using System.IO.Compression; using System.Linq; -using System.Net.Http.Headers; using System.Text.Json; using System.Text.Json.Serialization; +using static BBDown.Core.Util.HTTPUtil; using static BBDown.Core.Logger; namespace BBDown.Core @@ -322,7 +322,7 @@ private static string GenerateFawkesReqBin() /// /// /// 字节流 - private static byte[] ReadMessage(byte[] data) + public static byte[] ReadMessage(byte[] data) { byte first; int size; @@ -348,7 +348,7 @@ private static (byte first, int size) ReadInfo(byte[] data) /// /// /// - private static byte[] PackMessage(byte[] input) + public static byte[] PackMessage(byte[] input) { using var stream = new MemoryStream(); using (var writer = new BinaryWriter(stream)) @@ -393,31 +393,6 @@ private static byte[] GzipDecompress(byte[] data) } return output.ToArray(); } - - public static async Task GetPostResponseAsync(string Url, byte[] postData, Dictionary headers) - { - LogDebug("Post to: {0}, data: {1}", Url, Convert.ToBase64String(postData)); - - ByteArrayContent content = new(postData); - content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/grpc"); - - HttpRequestMessage request = new() - { - RequestUri = new Uri(Url), - Method = HttpMethod.Post, - Content = content, - //Version = HttpVersion.Version20 - }; - - if (headers != null) - foreach (KeyValuePair header in headers) - request.Headers.TryAddWithoutValidation(header.Key, header.Value); - - HttpResponseMessage response = await Util.HTTPUtil.AppHttpClient.SendAsync(request); - byte[] bytes = await response.Content.ReadAsByteArrayAsync(); - - return bytes; - } } diff --git a/BBDown.Core/Util/HTTPUtil.cs b/BBDown.Core/Util/HTTPUtil.cs index c80e953b2..77342c47c 100644 --- a/BBDown.Core/Util/HTTPUtil.cs +++ b/BBDown.Core/Util/HTTPUtil.cs @@ -70,18 +70,36 @@ public static async Task GetWebLocationAsync(string url) return location; } - public static async Task GetPostResponseAsync(string Url, byte[] postData) + public static async Task GetPostResponseAsync(string Url, byte[] postData, Dictionary headers = null) { LogDebug("Post to: {0}, data: {1}", Url, Convert.ToBase64String(postData)); - using HttpRequestMessage request = new(HttpMethod.Post, Url); - request.Headers.TryAddWithoutValidation("Content-Type", "application/grpc"); - request.Headers.TryAddWithoutValidation("Content-Length", postData.Length.ToString()); - request.Headers.TryAddWithoutValidation("User-Agent", "Dalvik/2.1.0 (Linux; U; Android 6.0.1; oneplus a5010 Build/V417IR) 6.10.0 os/android model/oneplus a5010 mobi_app/android build/6100500 channel/bili innerVer/6100500 osVer/6.0.1 network/2"); - request.Headers.TryAddWithoutValidation("Cookie", Config.COOKIE); - request.Content = new ByteArrayContent(postData); - var webResponse = await AppHttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); - string htmlCode = await webResponse.Content.ReadAsStringAsync(); - return htmlCode; + + ByteArrayContent content = new(postData); + content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/grpc"); + + HttpRequestMessage request = new() + { + RequestUri = new Uri(Url), + Method = HttpMethod.Post, + Content = content, + //Version = HttpVersion.Version20 + }; + + if (headers != null) + { + foreach (KeyValuePair header in headers) + request.Headers.TryAddWithoutValidation(header.Key, header.Value); + } + else + { + request.Headers.TryAddWithoutValidation("User-Agent", "Dalvik/2.1.0 (Linux; U; Android 6.0.1; oneplus a5010 Build/V417IR) 6.10.0 os/android model/oneplus a5010 mobi_app/android build/6100500 channel/bili innerVer/6100500 osVer/6.0.1 network/2"); + request.Headers.TryAddWithoutValidation("grpc-encoding", "gzip"); + } + + HttpResponseMessage response = await AppHttpClient.SendAsync(request); + byte[] bytes = await response.Content.ReadAsByteArrayAsync(); + + return bytes; } } } diff --git a/BBDown.Core/Util/SubUtil.cs b/BBDown.Core/Util/SubUtil.cs index 641ac4028..32e903494 100644 --- a/BBDown.Core/Util/SubUtil.cs +++ b/BBDown.Core/Util/SubUtil.cs @@ -1,4 +1,6 @@ -using System.Text; +using BBDown.Core.Protobuf; +using Google.Protobuf; +using System.Text; using static BBDown.Core.Entity.Entity; using static BBDown.Core.Util.HTTPUtil; using System.Text.RegularExpressions; @@ -356,6 +358,18 @@ public static (string, string) GetSubtitleCode(string key) } } + private static byte[] GetPayload(long aid, long cid) + { + var obj = new DmViewReq + { + Pid = aid, + Oid = cid, + Type = 1, + Spmid = "main.ugc-video-detail.0.0", + }; + return AppHelper.PackMessage(obj.ToByteArray()); + } + private static async Task?> GetSubtitlesFromApi3Async(string aid, string cid, string epId, int index) { try @@ -363,41 +377,20 @@ public static (string, string) GetSubtitleCode(string key) List subtitles = new(); //grpc调用接口 protobuf string api = "https://app.biliapi.net/bilibili.community.service.dm.v1.DM/DmView"; - int _aid = Convert.ToInt32(aid); - int _cid = Convert.ToInt32(cid); - int _type = 1; - byte[] data = new byte[18]; - data[0] = 0x0; data[1] = 0x0; data[2] = 0x0; data[3] = 0x0; data[4] = 0xD; //先固定死了 - int i = 5; - data[i++] = Convert.ToByte((1 << 3) | 0); // index=1 - while ((_aid & -128) != 0) - { - data[i++] = Convert.ToByte((_aid & 127) | 128); - _aid >>= 7; - } - data[i++] = Convert.ToByte(_aid); - data[i++] = Convert.ToByte((2 << 3) | 0); // index=2 - while ((_cid & -128) != 0) + + var data = GetPayload(Convert.ToInt64(aid), Convert.ToInt64(cid)); + + var t = AppHelper.ReadMessage(await GetPostResponseAsync(api, data)); + var resp = new MessageParser(() => new DmViewReply()).ParseFrom(t); + + if (resp.Subtitle != null && resp.Subtitle.Subtitles != null) { - data[i++] = Convert.ToByte((_cid & 127) | 128); - _cid >>= 7; + subtitles.AddRange(resp.Subtitle.Subtitles.Select(item => new Subtitle() { + url = item.SubtitleUrl, + lan = item.Lan, + path = $"{aid}/{aid}.{cid}.{item.Lan}.srt" + })); } - data[i++] = Convert.ToByte(_cid); - data[i++] = Convert.ToByte((3 << 3) | 0); // index=3 - data[i++] = Convert.ToByte(_type); - string t = await GetPostResponseAsync(api, data); - Regex reg = CnJsonRegex(); - foreach (Match m in reg.Matches(t).Cast()) - { - Subtitle subtitle = new() - { - url = m.Groups[2].Value, - lan = m.Groups[1].Value, - path = $"{aid}/{aid}.{cid}.{m.Groups[1].Value}.srt" - }; - subtitles.Add(subtitle); - } - //有空的URL 不合法 if (subtitles.Any(s => string.IsNullOrEmpty(s.url))) throw new Exception("Bad url"); @@ -421,9 +414,17 @@ public static async Task> GetSubtitlesAsync(string aid, string ci } else { - subtitles = await GetSubtitlesFromApi2Async(aid, cid, epId, index) - ?? await GetSubtitlesFromApi1Async(aid, cid, epId, index) - ?? await GetSubtitlesFromApi3Async(aid, cid, epId, index); + if (Config.COOKIE == "") + { + subtitles = await GetSubtitlesFromApi3Async(aid, cid, epId, index); // 未登录只有APP可以拿到字幕了 + } + else + { + subtitles = await GetSubtitlesFromApi2Async(aid, cid, epId, index) + ?? await GetSubtitlesFromApi1Async(aid, cid, epId, index) + ?? await GetSubtitlesFromApi3Async(aid, cid, epId, index); + } + } if (subtitles == null) @@ -480,7 +481,5 @@ private static string FormatTime(double sec) //64.13 [GeneratedRegex("-[a-z]")] private static partial Regex NonCapsRegex(); - [GeneratedRegex("(zh-Han[st]).*?(http.*?\\.json)")] - private static partial Regex CnJsonRegex(); } }