From 3f0ad0112df4820b6901e95b3e5daf4e6493a16d Mon Sep 17 00:00:00 2001 From: cxfksword <718792+cxfksword@users.noreply.github.com> Date: Sun, 5 Mar 2023 15:53:56 +0800 Subject: [PATCH] Optimize identity --- .../ParseNameTest.cs | 21 +++++++++ .../SeasonProviderTest.cs | 2 +- Jellyfin.Plugin.MetaShark/Core/NameParser.cs | 45 ++++++++++++++----- .../Providers/BaseProvider.cs | 38 +++++++++++++--- .../Providers/SeasonProvider.cs | 2 +- 5 files changed, 91 insertions(+), 17 deletions(-) diff --git a/Jellyfin.Plugin.MetaShark.Test/ParseNameTest.cs b/Jellyfin.Plugin.MetaShark.Test/ParseNameTest.cs index 5f0b5a9..afdd866 100644 --- a/Jellyfin.Plugin.MetaShark.Test/ParseNameTest.cs +++ b/Jellyfin.Plugin.MetaShark.Test/ParseNameTest.cs @@ -176,6 +176,20 @@ namespace Jellyfin.Plugin.MetaShark.Test Assert.AreEqual(parseResult.ParentIndexNumber, 1); Assert.AreEqual(parseResult.IndexNumber, 1); + + // 日文 + fileName = "プロポーズ大作戦Ep05_x264.mp4"; + parseResult = NameParser.Parse(fileName); + Assert.AreEqual(parseResult.Name, "プロポーズ大作戦Ep05"); + Assert.AreEqual(parseResult.ParentIndexNumber, null); + Assert.AreEqual(parseResult.IndexNumber, 5); + + fileName = "[01] [ANK-Raws] あっちこっち 01 (BDrip 1920x1080 HEVC-YUV420P10 FLAC)"; + parseResult = NameParser.Parse(fileName); + Assert.AreEqual(parseResult.Name, "あっちこっち 01"); + Assert.AreEqual(parseResult.ParentIndexNumber, null); + Assert.AreEqual(parseResult.IndexNumber, 1); + // 只中文 fileName = "齊天大聖 第02集"; parseResult = NameParser.Parse(fileName); @@ -183,6 +197,13 @@ namespace Jellyfin.Plugin.MetaShark.Test Assert.AreEqual(parseResult.ParentIndexNumber, null); Assert.AreEqual(parseResult.IndexNumber, 2); + fileName = "齊天大聖 第 02 期"; + parseResult = NameParser.Parse(fileName); + Assert.AreEqual(parseResult.Name, "齊天大聖"); + Assert.AreEqual(parseResult.ParentIndexNumber, null); + Assert.AreEqual(parseResult.IndexNumber, 2); + + // anime fileName = "[YYDM-11FANS][THERMAE_ROMAE][02][BDRIP][720P][X264-10bit_AAC][7FF2269F]"; parseResult = NameParser.Parse(fileName); diff --git a/Jellyfin.Plugin.MetaShark.Test/SeasonProviderTest.cs b/Jellyfin.Plugin.MetaShark.Test/SeasonProviderTest.cs index 2a63f92..53a8798 100644 --- a/Jellyfin.Plugin.MetaShark.Test/SeasonProviderTest.cs +++ b/Jellyfin.Plugin.MetaShark.Test/SeasonProviderTest.cs @@ -97,7 +97,7 @@ namespace Jellyfin.Plugin.MetaShark.Test Task.Run(async () => { var provider = new SeasonProvider(httpClientFactory, loggerFactory, libraryManagerStub.Object, httpContextAccessorStub.Object, doubanApi, tmdbApi, omdbApi); - var result = await provider.GuestDoubanSeasonByYearAsync("机动战士高达0083 星尘的回忆", 1991, CancellationToken.None); + var result = await provider.GuestDoubanSeasonByYearAsync("机动战士高达0083 星尘的回忆", 1991, null, CancellationToken.None); Assert.AreEqual(result, "1766564"); }).GetAwaiter().GetResult(); } diff --git a/Jellyfin.Plugin.MetaShark/Core/NameParser.cs b/Jellyfin.Plugin.MetaShark/Core/NameParser.cs index b90771f..1743e21 100644 --- a/Jellyfin.Plugin.MetaShark/Core/NameParser.cs +++ b/Jellyfin.Plugin.MetaShark/Core/NameParser.cs @@ -13,17 +13,22 @@ namespace Jellyfin.Plugin.MetaShark.Core { private static readonly Regex yearReg = new Regex(@"[12][890][0-9][0-9]", RegexOptions.Compiled); private static readonly Regex seasonSuffixReg = new Regex(@"[ .]S\d{1,2}$", RegexOptions.Compiled); - private static readonly Regex unusedReg = new Regex(@"\[.+?\]|\(.+?\)|【.+?】", RegexOptions.Compiled); private static readonly Regex fixSeasonNumberReg = new Regex(@"(\[|\.)S(\d{1,2})(\]|\.)", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex startWithHyphenCharReg = new Regex(@"^[-~~]", RegexOptions.Compiled); - private static readonly Regex chineseIndexNumberReg = new Regex(@"第([0-9零一二三四五六七八九]+?)(集|章|话|話)", RegexOptions.Compiled); + private static readonly Regex chineseIndexNumberReg = new Regex(@"第\s*?([0-9零一二三四五六七八九]+?)\s*?(集|章|话|話|期)", RegexOptions.Compiled); - public static ParseNameResult Parse(string fileName, bool isTvSeries = false) + private static readonly Regex normalizeNameReg = new Regex(@"第\s*?([0-9零一二三四五六七八九]+?)\s*?(集|章|话|話|期)", RegexOptions.Compiled); + + private static readonly Regex specialIndexNumberReg = new Regex(@"ep(\d{1,2})", RegexOptions.Compiled | RegexOptions.IgnoreCase); + + public static ParseNameResult Parse(string fileName, bool isEpisode = false) { + fileName = NormalizeFileName(fileName); + var parseResult = new ParseNameResult(); var anitomyResult = AnitomySharp.AnitomySharp.Parse(fileName); var isAnime = IsAnime(fileName); @@ -110,13 +115,13 @@ namespace Jellyfin.Plugin.MetaShark.Core } } - // 修复纯中文集数 + // 修复纯中文集数/特殊标识集数 if (parseResult.IndexNumber is null) { - parseResult.IndexNumber = ParseChineseIndexNumber(fileName); + parseResult.IndexNumber = ParseChineseOrSpecialIndexNumber(fileName); } - // 解析不到title时,使用默认名 + // 解析不到title时,或解析出多个title时,使用默认名 if (string.IsNullOrEmpty(parseResult.Name)) { parseResult.Name = fileName; @@ -167,14 +172,22 @@ namespace Jellyfin.Plugin.MetaShark.Core return 0; } - private static int? ParseChineseIndexNumber(string fileName) + private static string NormalizeFileName(string fileName) + { + // 去掉中文集数之间的空格(要不然Anitomy解析不正确) + fileName = normalizeNameReg.Replace(fileName, m => m.Value.Replace(" ", "")); + + return fileName; + } + + private static int? ParseChineseOrSpecialIndexNumber(string fileName) { var match = chineseIndexNumberReg.Match(fileName); if (match.Success && match.Groups.Count > 1) { - if (int.TryParse(match.Groups[1].Value, out var seasonNumber)) + if (int.TryParse(match.Groups[1].Value, out var indexNumber)) { - return seasonNumber; + return indexNumber; } var number = Utils.ChineseNumberToInt(match.Groups[1].Value); @@ -183,6 +196,18 @@ namespace Jellyfin.Plugin.MetaShark.Core return number; } } + else + { + match = specialIndexNumberReg.Match(fileName); + if (match.Success && match.Groups.Count > 1) + { + if (int.TryParse(match.Groups[1].Value, out var indexNumber)) + { + return indexNumber; + } + } + } + return null; } @@ -196,7 +221,7 @@ namespace Jellyfin.Plugin.MetaShark.Core } var folder = Path.GetFileName(Path.GetDirectoryName(path)) ?? string.Empty; - return folder == "SPs" || folder.Contains("特典"); + return folder == "SPs" || folder == "Specials" || folder.Contains("特典"); } diff --git a/Jellyfin.Plugin.MetaShark/Providers/BaseProvider.cs b/Jellyfin.Plugin.MetaShark/Providers/BaseProvider.cs index b708255..28f924e 100644 --- a/Jellyfin.Plugin.MetaShark/Providers/BaseProvider.cs +++ b/Jellyfin.Plugin.MetaShark/Providers/BaseProvider.cs @@ -171,20 +171,20 @@ namespace Jellyfin.Plugin.MetaShark.Providers return null; } - public async Task GuestDoubanSeasonByYearAsync(string name, int? year, CancellationToken cancellationToken) + public async Task GuestDoubanSeasonByYearAsync(string seriesName, int? year, int? seasonNumber, CancellationToken cancellationToken) { if (year == null || year == 0) { return null; } - this.Log($"GuestDoubanSeasonByYear of [name]: {name} [year]: {year}"); + this.Log($"GuestDoubanSeasonByYear of [name]: {seriesName} [year]: {year}"); // 先通过suggest接口查找,减少搜索页访问次数,避免封禁(suggest没法区分电影或电视剧,排序也比搜索页差些) if (config.EnableDoubanAvoidRiskControl) { - var suggestResult = await this._doubanApi.SearchBySuggestAsync(name, cancellationToken).ConfigureAwait(false); - var suggestItem = suggestResult.Where(x => x.Year == year && x.Name == name).FirstOrDefault(); + var suggestResult = await this._doubanApi.SearchBySuggestAsync(seriesName, cancellationToken).ConfigureAwait(false); + var suggestItem = suggestResult.Where(x => x.Year == year && x.Name == seriesName).FirstOrDefault(); if (suggestItem != null) { this.Log($"Found douban [id]: {suggestItem.Name}({suggestItem.Sid}) (suggest)"); @@ -200,10 +200,18 @@ namespace Jellyfin.Plugin.MetaShark.Providers // 通过搜索页面查找 - var result = await this._doubanApi.SearchAsync(name, cancellationToken).ConfigureAwait(false); + var result = await this._doubanApi.SearchAsync(seriesName, cancellationToken).ConfigureAwait(false); var item = result.Where(x => x.Category == "电视剧" && x.Year == year).FirstOrDefault(); if (item != null && !string.IsNullOrEmpty(item.Sid)) { + // 判断名称中是否有第X季,有的话和seasonNumber比较,用于修正多季都在同一年时,每次都是错误取第一个的情况 + var nameIndexNumber = ParseChineseSeasonNumberByName(item.Name); + if (nameIndexNumber.HasValue && seasonNumber.HasValue && nameIndexNumber != seasonNumber) + { + this.Log($"GuestDoubanSeasonByYear not found!"); + return null; + } + this.Log($"Found douban [id]: {item.Name}({item.Sid})"); return item.Sid; } @@ -380,6 +388,26 @@ namespace Jellyfin.Plugin.MetaShark.Providers } + public int? ParseChineseSeasonNumberByName(string name) + { + var regSeason = new Regex(@"\s第([0-9零一二三四五六七八九]+?)(季|部)", RegexOptions.Compiled); + var match = regSeason.Match(name); + if (match.Success && match.Groups.Count > 1) + { + var seasonNumber = match.Groups[1].Value.ToInt(); + if (seasonNumber <= 0) + { + seasonNumber = Utils.ChineseNumberToInt(match.Groups[1].Value) ?? 0; + } + if (seasonNumber > 0) + { + return seasonNumber; + } + } + return null; + } + + /// /// 浏览器来源请求,返回代理地址(no-referer对于background-image不生效),其他客户端请求,返回原始图片地址 /// diff --git a/Jellyfin.Plugin.MetaShark/Providers/SeasonProvider.cs b/Jellyfin.Plugin.MetaShark/Providers/SeasonProvider.cs index 6c5654b..b965c39 100644 --- a/Jellyfin.Plugin.MetaShark/Providers/SeasonProvider.cs +++ b/Jellyfin.Plugin.MetaShark/Providers/SeasonProvider.cs @@ -165,7 +165,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers if (!string.IsNullOrEmpty(seriesName) && seasonYear > 0) { - var seasonSid = await this.GuestDoubanSeasonByYearAsync(seriesName, seasonYear, cancellationToken).ConfigureAwait(false); + var seasonSid = await this.GuestDoubanSeasonByYearAsync(seriesName, seasonYear, seasonNumber, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrEmpty(seasonSid)) { return seasonSid;