From 35ea11883ff02b31ca43bd3f8a7f53918dc7517a Mon Sep 17 00:00:00 2001 From: Meiam <91270@qq.com> Date: Mon, 22 Dec 2025 18:06:54 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=8A=A0=E5=9B=BA=E5=B0=84=E6=89=8B?= =?UTF-8?q?=E7=BD=91=20API=20=E7=A8=B3=E5=AE=9A=E6=80=A7=E5=B9=B6=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E5=85=A8=E9=87=8F=E4=BB=A3=E7=A0=81=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复:针对射手网 API 年久失修、无结果时返回乱码或非法内容的问题,增加了 JSON 合法性校验逻辑,确保插件在异常返回下能静默退出而不崩溃。 - 优化:执行了全量代码格式化 (dotnet format),确保缩进、换行及代码风格符合 .NET 官方规范。 - 维护:清理了所有项目中不再使用的提示性条目逻辑,保持代码简洁。 --- Emby.MeiamSub.DevTool/Program.cs | 14 ++++- Emby.MeiamSub.Shooter/ShooterProvider.cs | 55 +++++++++++------ .../Model/SubtitleResponseRoot.cs | 2 +- Emby.MeiamSub.Thunder/ThunderProvider.cs | 35 +++++------ Jellyfin.MeiamSub.Shooter/ShooterProvider.cs | 60 ++++++++++++++----- Jellyfin.MeiamSub.Thunder/ThunderProvider.cs | 23 +++---- 6 files changed, 125 insertions(+), 64 deletions(-) diff --git a/Emby.MeiamSub.DevTool/Program.cs b/Emby.MeiamSub.DevTool/Program.cs index ffc9a4f..d315256 100644 --- a/Emby.MeiamSub.DevTool/Program.cs +++ b/Emby.MeiamSub.DevTool/Program.cs @@ -20,7 +20,7 @@ namespace Emby.Subtitle.DevTool Console.OutputEncoding = Encoding.UTF8; Console.WriteLine("================ MeiamSubtitles 调试工具 ================"); - + // 待测试的影音文件路径 var testFilePath = @"D:\Source\MeiamSubtitles\TestServer\Movie\2009\三傻大闹宝莱坞\三傻大闹宝莱坞 (2009) - 1080p.mkv"; @@ -69,7 +69,15 @@ namespace Emby.Subtitle.DevTool var response = await _httpClient.PostAsync(url, content); var result = await response.Content.ReadAsStringAsync(); Console.WriteLine($" > STATUS: {response.StatusCode}"); - Console.WriteLine($" > RETURN: {result}"); + + if (!result.Trim().StartsWith("[")) + { + Console.WriteLine($" > [警告] API 返回了非法内容 (可能已失效或乱码): {result}"); + } + else + { + Console.WriteLine($" > RETURN: {result}"); + } } catch (Exception ex) { @@ -84,7 +92,7 @@ namespace Emby.Subtitle.DevTool // 迅雷搜索接口通常基于文件名,CID 用于后续匹配校验 var fileName = Path.GetFileName(filePath); var url = $"https://api-shoulei-ssl.xunlei.com/oracle/subtitle?name={HttpUtility.UrlEncode(fileName)}"; - + var request = new HttpRequestMessage(HttpMethod.Get, url); request.Headers.Add("User-Agent", "MeiamSub.Thunder"); diff --git a/Emby.MeiamSub.Shooter/ShooterProvider.cs b/Emby.MeiamSub.Shooter/ShooterProvider.cs index d65a217..0266150 100644 --- a/Emby.MeiamSub.Shooter/ShooterProvider.cs +++ b/Emby.MeiamSub.Shooter/ShooterProvider.cs @@ -45,7 +45,7 @@ namespace Emby.MeiamSub.Shooter #endregion #region 构造函数 - public ShooterProvider(ILogManager logManager, IJsonSerializer jsonSerializer,IHttpClient httpClient) + public ShooterProvider(ILogManager logManager, IJsonSerializer jsonSerializer, IHttpClient httpClient) { _logger = logManager.GetLogger(GetType().Name); _jsonSerializer = jsonSerializer; @@ -65,7 +65,7 @@ namespace Emby.MeiamSub.Shooter /// 远程字幕信息列表 public async Task> Search(SubtitleSearchRequest request, CancellationToken cancellationToken) { - _logger.Info("{0} Search | SubtitleSearchRequest -> {1}", new object[2] { Name , _jsonSerializer.SerializeToString(request) }); + _logger.Info("{0} Search | SubtitleSearchRequest -> {1}", new object[2] { Name, _jsonSerializer.SerializeToString(request) }); var subtitles = await SearchSubtitlesAsync(request); @@ -127,7 +127,25 @@ namespace Emby.MeiamSub.Shooter if (response.StatusCode == HttpStatusCode.OK && response.ContentType.Contains("application/json")) { - var subtitleResponse = _jsonSerializer.DeserializeFromStream>(response.Content); + // 修改人: Meiam + // 修改时间: 2025-12-22 + // 备注: 增加对射手网 API 返回非法内容(如乱码)的校验 + + string responseBody; + using (var reader = new StreamReader(response.Content, Encoding.UTF8)) + { + responseBody = await reader.ReadToEndAsync(); + } + + _logger.Info("{0} Search | ResponseBody -> {1}", new object[2] { Name, responseBody }); + + if (string.IsNullOrEmpty(responseBody) || !responseBody.Trim().StartsWith("[")) + { + _logger.Info("{0} Search | Summary -> API returned invalid content (likely no subtitles found or API error).", Name); + return Array.Empty(); + } + + var subtitleResponse = _jsonSerializer.DeserializeFromString>(responseBody); if (subtitleResponse != null) { @@ -141,21 +159,22 @@ namespace Emby.MeiamSub.Shooter { remoteSubtitles.Add(new RemoteSubtitleInfo() { - Id = Base64Encode(_jsonSerializer.SerializeToString(new DownloadSubInfo - { - Url = subFile.Link, - Format = subFile.Ext, + Id = Base64Encode(_jsonSerializer.SerializeToString(new DownloadSubInfo + { + Url = subFile.Link, + Format = subFile.Ext, + Language = request.Language, + IsForced = request.IsForced + })), + Name = $"[MEIAMSUB] {Path.GetFileName(request.MediaPath)} | {request.Language} | 射手", Language = request.Language, - IsForced = request.IsForced - })), - Name = $"[MEIAMSUB] {Path.GetFileName(request.MediaPath)} | {request.Language} | 射手", - Language = request.Language, - Author = "Meiam ", - ProviderName = $"{Name}", - Format = subFile.Ext, - Comment = $"Format : {ExtractFormat(subFile.Ext)}", - IsHashMatch = true - }); } + Author = "Meiam ", + ProviderName = $"{Name}", + Format = subFile.Ext, + Comment = $"Format : {ExtractFormat(subFile.Ext)}", + IsHashMatch = true + }); + } } _logger.Info("{0} Search | Summary -> Get {1} Subtitles", new object[2] { Name, remoteSubtitles.Count }); @@ -286,7 +305,7 @@ namespace Emby.MeiamSub.Shooter if (text.Contains(ASS)) return ASS; if (text.Contains(SSA)) return SSA; if (text.Contains(SRT)) return SRT; - + return null; } diff --git a/Emby.MeiamSub.Thunder/Model/SubtitleResponseRoot.cs b/Emby.MeiamSub.Thunder/Model/SubtitleResponseRoot.cs index 3392751..be03ec9 100644 --- a/Emby.MeiamSub.Thunder/Model/SubtitleResponseRoot.cs +++ b/Emby.MeiamSub.Thunder/Model/SubtitleResponseRoot.cs @@ -20,7 +20,7 @@ namespace Emby.MeiamSub.Thunder.Model public int Duration { get; set; } public string[] Languages { get; set; } - public string Langs=> Languages != null ? string.Join(",", Languages) : string.Empty; + public string Langs => Languages != null ? string.Join(",", Languages) : string.Empty; public int Source { get; set; } public int Score { get; set; } diff --git a/Emby.MeiamSub.Thunder/ThunderProvider.cs b/Emby.MeiamSub.Thunder/ThunderProvider.cs index 2dc4e59..80829f0 100644 --- a/Emby.MeiamSub.Thunder/ThunderProvider.cs +++ b/Emby.MeiamSub.Thunder/ThunderProvider.cs @@ -132,21 +132,22 @@ namespace Emby.MeiamSub.Thunder { remoteSubtitles.Add(new RemoteSubtitleInfo() { - Id = Base64Encode(_jsonSerializer.SerializeToString(new DownloadSubInfo - { - Url = item.Url, - Format = item.Ext, - Language = request.Language, - IsForced = request.IsForced - })), - Name = $"[MEIAMSUB] {item.Name} | {(item.Langs == string.Empty ? "未知" : item.Langs)} | 迅雷", - Language = request.Language, - Author = "Meiam ", - ProviderName = $"{Name}", - Format = item.Ext, - Comment = $"Format : {item.Ext}", - IsHashMatch = cid == item.Cid, - }); } + Id = Base64Encode(_jsonSerializer.SerializeToString(new DownloadSubInfo + { + Url = item.Url, + Format = item.Ext, + Language = request.Language, + IsForced = request.IsForced + })), + Name = $"[MEIAMSUB] {item.Name} | {(item.Langs == string.Empty ? "未知" : item.Langs)} | 迅雷", + Language = request.Language, + Author = "Meiam ", + ProviderName = $"{Name}", + Format = item.Ext, + Comment = $"Format : {item.Ext}", + IsHashMatch = cid == item.Cid, + }); + } } _logger.Info("{0} Search | Summary -> Get {1} Subtitles", new object[2] { Name, remoteSubtitles.Count }); @@ -177,7 +178,7 @@ namespace Emby.MeiamSub.Thunder /// 包含字幕流的响应对象 public async Task GetSubtitles(string id, CancellationToken cancellationToken) { - _logger.Info("{0} DownloadSub | Request -> {1}", new object[2] { Name, id }); + _logger.Info("{0} DownloadSub | Request -> {1}", new object[2] { Name, id }); return await DownloadSubAsync(id); } @@ -324,7 +325,7 @@ namespace Emby.MeiamSub.Thunder { // 使用 BinaryReader 配合 BaseStream 需要小心,因为 BinaryReader 本身不支持异步 Read // 这里我们直接操作 stream 进行异步读取,不再使用 BinaryReader,因为只是读取字节数组 - + var fileSize = new FileInfo(filePath).Length; using (var sha1 = SHA1.Create()) { diff --git a/Jellyfin.MeiamSub.Shooter/ShooterProvider.cs b/Jellyfin.MeiamSub.Shooter/ShooterProvider.cs index f67db0c..8cf44c3 100644 --- a/Jellyfin.MeiamSub.Shooter/ShooterProvider.cs +++ b/Jellyfin.MeiamSub.Shooter/ShooterProvider.cs @@ -71,7 +71,7 @@ namespace Jellyfin.MeiamSub.Shooter /// 远程字幕信息列表 public async Task> Search(SubtitleSearchRequest request, CancellationToken cancellationToken) { - _logger.LogInformation($"{Name} Search | SubtitleSearchRequest -> { JsonSerializer.Serialize(request) }"); + _logger.LogInformation($"{Name} Search | SubtitleSearchRequest -> {JsonSerializer.Serialize(request)}"); var subtitles = await SearchSubtitlesAsync(request); @@ -130,12 +130,43 @@ namespace Jellyfin.MeiamSub.Shooter _logger.LogInformation($"{Name} Search | Response -> {JsonSerializer.Serialize(response)}"); // 处理响应 + if (response.IsSuccessStatusCode && response.Content.Headers.Any(m => m.Value.Contains("application/json; charset=utf-8"))) + { + var responseBody = await response.Content.ReadAsStringAsync(); + + _logger.LogInformation($"{Name} Search | ResponseBody -> {responseBody} "); + + + if (string.IsNullOrEmpty(responseBody) || !responseBody.Trim().StartsWith("[")) + + + + { + + + + _logger.LogInformation($"{Name} Search | Summary -> API returned invalid content (likely no subtitles found or API error)."); + + + + return Array.Empty(); + + + + } + + + + + + + var subtitles = JsonSerializer.Deserialize>(responseBody); _logger.LogInformation($"{Name} Search | Response -> {JsonSerializer.Serialize(subtitles)}"); @@ -151,20 +182,21 @@ namespace Jellyfin.MeiamSub.Shooter { remoteSubtitles.Add(new RemoteSubtitleInfo() { - Id = Base64Encode(JsonSerializer.Serialize(new DownloadSubInfo - { - Url = subFile.Link, + Id = Base64Encode(JsonSerializer.Serialize(new DownloadSubInfo + { + Url = subFile.Link, + Format = subFile.Ext, + Language = request.Language, + TwoLetterISOLanguageName = request.TwoLetterISOLanguageName, + })), + Name = $"[MEIAMSUB] {Path.GetFileName(request.MediaPath)} | {request.TwoLetterISOLanguageName} | 射手", + Author = "Meiam ", + ProviderName = $"{Name}", Format = subFile.Ext, - Language = request.Language, - TwoLetterISOLanguageName = request.TwoLetterISOLanguageName, - })), - Name = $"[MEIAMSUB] {Path.GetFileName(request.MediaPath)} | {request.TwoLetterISOLanguageName} | 射手", - Author = "Meiam ", - ProviderName = $"{Name}", - Format = subFile.Ext, - Comment = $"Format : {ExtractFormat(subFile.Ext)}", - IsHashMatch = true - }); } + Comment = $"Format : {ExtractFormat(subFile.Ext)}", + IsHashMatch = true + }); + } } _logger.LogInformation($"{Name} Search | Summary -> Get {remoteSubtitles.Count} Subtitles"); diff --git a/Jellyfin.MeiamSub.Thunder/ThunderProvider.cs b/Jellyfin.MeiamSub.Thunder/ThunderProvider.cs index b7e1d9d..b6b079c 100644 --- a/Jellyfin.MeiamSub.Thunder/ThunderProvider.cs +++ b/Jellyfin.MeiamSub.Thunder/ThunderProvider.cs @@ -68,7 +68,7 @@ namespace Jellyfin.MeiamSub.Thunder /// 远程字幕信息列表 public async Task> Search(SubtitleSearchRequest request, CancellationToken cancellationToken) { - _logger.LogInformation($"{Name} Search | SubtitleSearchRequest -> { JsonSerializer.Serialize(request) }"); + _logger.LogInformation($"{Name} Search | SubtitleSearchRequest -> {JsonSerializer.Serialize(request)}"); var subtitles = await SearchSubtitlesAsync(request); @@ -138,16 +138,17 @@ namespace Jellyfin.MeiamSub.Thunder { Url = item.Url, Format = item.Ext, - Language = request.Language, - TwoLetterISOLanguageName = request.TwoLetterISOLanguageName, - })), - Name = $"[MEIAMSUB] {item.Name} | {(item.Langs == string.Empty ? "未知" : item.Langs)} | 迅雷", - Author = "Meiam ", - ProviderName = $"{Name}", - Format = item.Ext, - Comment = $"Format : {item.Ext}", - IsHashMatch = cid == item.Cid, - }); } + Language = request.Language, + TwoLetterISOLanguageName = request.TwoLetterISOLanguageName, + })), + Name = $"[MEIAMSUB] {item.Name} | {(item.Langs == string.Empty ? "未知" : item.Langs)} | 迅雷", + Author = "Meiam ", + ProviderName = $"{Name}", + Format = item.Ext, + Comment = $"Format : {item.Ext}", + IsHashMatch = cid == item.Cid, + }); + } } _logger.LogInformation($"{Name} Search | Summary -> Get {subtitles.Count()} Subtitles");