From d4c458975841e19180774e4d14afe7983d597776 Mon Sep 17 00:00:00 2001 From: cxfksword <718792+cxfksword@users.noreply.github.com> Date: Sat, 18 Feb 2023 21:47:24 +0800 Subject: [PATCH] Optimize identity --- .../ParseNameTest.cs | 30 ++++++++++ Jellyfin.Plugin.MetaShark/Api/TmdbApi.cs | 3 +- Jellyfin.Plugin.MetaShark/Core/NameParser.cs | 28 +++++++++- Jellyfin.Plugin.MetaShark/Model/GuessInfo.cs | 2 + .../Providers/EpisodeProvider.cs | 56 ++++++++++++++++++- .../Providers/SeasonProvider.cs | 8 ++- .../Providers/SeriesProvider.cs | 1 + 7 files changed, 122 insertions(+), 6 deletions(-) diff --git a/Jellyfin.Plugin.MetaShark.Test/ParseNameTest.cs b/Jellyfin.Plugin.MetaShark.Test/ParseNameTest.cs index feeba9a..e83dae1 100644 --- a/Jellyfin.Plugin.MetaShark.Test/ParseNameTest.cs +++ b/Jellyfin.Plugin.MetaShark.Test/ParseNameTest.cs @@ -140,11 +140,41 @@ namespace Jellyfin.Plugin.MetaShark.Test parseResult = NameParser.Parse(fileName); Console.WriteLine(parseResult.ToJson()); + // anime带季数 fileName = "[WMSUB][Detective Conan - Zero‘s Tea Time ][S01][E06][BIG5][1080P].mp4"; parseResult = NameParser.Parse(fileName); Console.WriteLine(parseResult.ToJson()); + fileName = "[KTXP][Machikado_Mazoku_S2][01][BIG5][1080p]"; + parseResult = NameParser.Parse(fileName); + Console.WriteLine(parseResult.ToJson()); + + // anime特典 + fileName = "[KissSub][Steins;Gate][SP][GB_BIG5_JP][BDrip][1080P][HEVC] 边界曲面的缺失之环"; + parseResult = NameParser.Parse(fileName); + Console.WriteLine(parseResult.ToJson()); + + } + + [TestMethod] + public void TestCheckExtra() + { + var name = "[VCB-Studio] Spice and Wolf [CM02][Ma10p_1080p][x265_flac]"; + var result = NameParser.IsExtra(name); + Console.WriteLine(result); + + name = "[VCB-Studio] Spice and Wolf [Menu01_2][Ma10p_1080p][x265_flac]"; + result = NameParser.IsExtra(name); + Console.WriteLine(result); + + name = "[VCB-Studio] Spice and Wolf [NCED][Ma10p_1080p][x265_flac]"; + result = NameParser.IsExtra(name); + Console.WriteLine(result); + + name = "[VCB-Studio] Spice and Wolf [NCOP][Ma10p_1080p][x265_flac]"; + result = NameParser.IsExtra(name); + Console.WriteLine(result); } } diff --git a/Jellyfin.Plugin.MetaShark/Api/TmdbApi.cs b/Jellyfin.Plugin.MetaShark/Api/TmdbApi.cs index 2732812..0d89e46 100644 --- a/Jellyfin.Plugin.MetaShark/Api/TmdbApi.cs +++ b/Jellyfin.Plugin.MetaShark/Api/TmdbApi.cs @@ -209,7 +209,8 @@ namespace Jellyfin.Plugin.MetaShark.Api } catch (Exception ex) { - _memoryCache.Set(key, season, TimeSpan.FromHours(CacheDurationInHours)); + // 可能网络有问题,缓存一下避免频繁请求 + _memoryCache.Set(key, season, TimeSpan.FromSeconds(30)); this._logger.LogError(ex, ex.Message); return null; } diff --git a/Jellyfin.Plugin.MetaShark/Core/NameParser.cs b/Jellyfin.Plugin.MetaShark/Core/NameParser.cs index aa7a96b..4f9bebb 100644 --- a/Jellyfin.Plugin.MetaShark/Core/NameParser.cs +++ b/Jellyfin.Plugin.MetaShark/Core/NameParser.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; @@ -15,6 +16,8 @@ namespace Jellyfin.Plugin.MetaShark.Core private static readonly Regex unusedReg = new Regex(@"\[.+?\]|\(.+?\)|【.+?】", RegexOptions.Compiled); + private static readonly Regex extrasReg = new Regex(@"\[(CM|Menu|NCED|NCOP|Drama)[0-9_]*?\]", RegexOptions.Compiled); + public static ParseNameResult Parse(string fileName, bool isTvSeries = false) { var parseResult = new ParseNameResult(); @@ -136,10 +139,33 @@ namespace Jellyfin.Plugin.MetaShark.Core return 0; } + public static bool IsSpecial(string path) + { + var fileName = Path.GetFileNameWithoutExtension(path) ?? string.Empty; + if (IsAnime(fileName)) + { + if (fileName.Contains("[SP]")) + { + return true; + } + + string folder = Path.GetFileName(Path.GetDirectoryName(path)) ?? string.Empty; + return folder == "SPs" && !extrasReg.IsMatch(fileName); + } + + return false; + } + + public static bool IsExtra(string name) + { + return IsAnime(name) && extrasReg.IsMatch(name); + } + + // 判断是否为动漫 // https://github.com/jxxghp/nas-tools/blob/f549c924558fd49e183333285bc6a804af1a2cb7/app/media/meta/metainfo.py#L51 - private static bool IsAnime(string name) + public static bool IsAnime(string name) { if (string.IsNullOrWhiteSpace(name)) { diff --git a/Jellyfin.Plugin.MetaShark/Model/GuessInfo.cs b/Jellyfin.Plugin.MetaShark/Model/GuessInfo.cs index 964c9f6..31d5726 100644 --- a/Jellyfin.Plugin.MetaShark/Model/GuessInfo.cs +++ b/Jellyfin.Plugin.MetaShark/Model/GuessInfo.cs @@ -11,5 +11,7 @@ namespace Jellyfin.Plugin.MetaShark.Model public int? episodeNumber { get; set; } public int? seasonNumber { get; set; } + + public string? Name { get; set; } } } \ No newline at end of file diff --git a/Jellyfin.Plugin.MetaShark/Providers/EpisodeProvider.cs b/Jellyfin.Plugin.MetaShark/Providers/EpisodeProvider.cs index 88b5628..0995dd1 100644 --- a/Jellyfin.Plugin.MetaShark/Providers/EpisodeProvider.cs +++ b/Jellyfin.Plugin.MetaShark/Providers/EpisodeProvider.cs @@ -1,4 +1,5 @@ -using Jellyfin.Plugin.MetaShark.Api; +using System.Reflection.Metadata; +using Jellyfin.Plugin.MetaShark.Api; using Jellyfin.Plugin.MetaShark.Core; using Jellyfin.Plugin.MetaShark.Model; using MediaBrowser.Common.Net; @@ -62,6 +63,14 @@ namespace Jellyfin.Plugin.MetaShark.Providers this.Log($"GetEpisodeMetadata of [name]: {info.Name} number: {info.IndexNumber} ParentIndexNumber: {info.ParentIndexNumber}"); var result = new MetadataResult(); + // 动画特典和extras处理 + var specialEpisode = this.HandleAnimeSpecialAndExtras(info.Path); + if (specialEpisode != null) + { + result.HasMetadata = true; + result.Item = specialEpisode; + return result; + } // 剧集信息只有tmdb有 info.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out var seriesTmdbId); @@ -82,9 +91,9 @@ namespace Jellyfin.Plugin.MetaShark.Providers seasonNumber = 1; } // 修正anime命名格式导致的episodeNumber错误 - var fileName = Path.GetFileName(info.Path) ?? string.Empty; + var fileName = Path.GetFileNameWithoutExtension(info.Path) ?? string.Empty; var guessInfo = this.GuessEpisodeNumber(fileName); - this.Log("GuessEpisodeNumber: fileName: {0} seasonNumber: {1} episodeNumber: {2}", fileName, guessInfo.seasonNumber, guessInfo.episodeNumber); + this.Log("GuessEpisodeNumber: fileName: {0} seasonNumber: {1} episodeNumber: {2} name: {3}", fileName, guessInfo.seasonNumber, guessInfo.episodeNumber, guessInfo.Name); if (guessInfo.seasonNumber.HasValue && guessInfo.seasonNumber != seasonNumber) { seasonNumber = guessInfo.seasonNumber.Value; @@ -99,6 +108,10 @@ namespace Jellyfin.Plugin.MetaShark.Providers ParentIndexNumber = seasonNumber, IndexNumber = episodeNumber }; + if (!string.IsNullOrEmpty(guessInfo.Name)) + { + result.Item.Name = guessInfo.Name; + } } if (episodeNumber is null or 0 || seasonNumber is null or 0 || string.IsNullOrEmpty(seriesTmdbId)) @@ -197,9 +210,46 @@ namespace Jellyfin.Plugin.MetaShark.Providers guessInfo.episodeNumber = null; } + var animeName = parseResult.FirstOrDefault(x => x.Category == AnitomySharp.Element.ElementCategory.ElementAnimeTitle); + if (animeName != null && NameParser.IsAnime(fileName)) + { + guessInfo.Name = animeName.Value; + } + return guessInfo; } + private Episode? HandleAnimeSpecialAndExtras(string filePath) + { + var fileName = Path.GetFileNameWithoutExtension(filePath) ?? string.Empty; + if (NameParser.IsExtra(fileName)) + { + this.Log($"Found anime extra of [name]: {fileName}"); + return new Episode + { + Name = fileName + }; + } + if (NameParser.IsSpecial(filePath)) + { + this.Log($"Found anime sp of [name]: {fileName}"); + var guessInfo = this.GuessEpisodeNumber(fileName); + var ep = new Episode + { + ParentIndexNumber = 0, + IndexNumber = guessInfo.episodeNumber, + }; + if (!string.IsNullOrEmpty(guessInfo.Name)) + { + ep.Name = guessInfo.Name; + } + + return ep; + } + + return null; + } + protected int GetVideoFileCount(string? dir) diff --git a/Jellyfin.Plugin.MetaShark/Providers/SeasonProvider.cs b/Jellyfin.Plugin.MetaShark/Providers/SeasonProvider.cs index ed05a6c..a4e5e22 100644 --- a/Jellyfin.Plugin.MetaShark/Providers/SeasonProvider.cs +++ b/Jellyfin.Plugin.MetaShark/Providers/SeasonProvider.cs @@ -57,7 +57,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers if (metaSource != MetaSource.Tmdb && !string.IsNullOrEmpty(sid)) { - // 从sereis获取正确名称,info的季名称是第x季 + // 从sereis获取正确名称,info.Name当是标准格式如S01等时,会变成第x季,非标准名称没法识别时默认文件名 var series = await this._doubanApi.GetMovieAsync(sid, cancellationToken).ConfigureAwait(false); if (series == null) { @@ -65,6 +65,8 @@ namespace Jellyfin.Plugin.MetaShark.Providers } var seasonName = RemoveSeasonSubfix(series.Name); + // TODO:季文件夹名称不规范,没法拿到seasonNumber,尝试从文件名猜出??? + // 没有季id,但存在tmdbid,尝试从tmdb获取对应季的年份信息,用于从豆瓣搜索对应季数据 if (string.IsNullOrEmpty(seasonSid)) { @@ -119,6 +121,10 @@ namespace Jellyfin.Plugin.MetaShark.Providers return result; } } + else + { + this.Log($"GetSeasonMetaData of [name]: {info.Name} not found douban season id!"); + } // tmdb有数据,豆瓣找不到,尝试获取tmdb的季数据 diff --git a/Jellyfin.Plugin.MetaShark/Providers/SeriesProvider.cs b/Jellyfin.Plugin.MetaShark/Providers/SeriesProvider.cs index e35ff28..1be51a4 100644 --- a/Jellyfin.Plugin.MetaShark/Providers/SeriesProvider.cs +++ b/Jellyfin.Plugin.MetaShark/Providers/SeriesProvider.cs @@ -123,6 +123,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers Genres = subject.Genres, // ProductionLocations = [x?.Country], PremiereDate = subject.ScreenTime, + Tagline = string.Empty, }; if (!string.IsNullOrEmpty(subject.Imdb))