diff --git a/Jellyfin.Plugin.MetaShark.Test/EpisodeProviderTest.cs b/Jellyfin.Plugin.MetaShark.Test/EpisodeProviderTest.cs index 4ddb907..d83094d 100644 --- a/Jellyfin.Plugin.MetaShark.Test/EpisodeProviderTest.cs +++ b/Jellyfin.Plugin.MetaShark.Test/EpisodeProviderTest.cs @@ -10,6 +10,8 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Serialization; namespace Jellyfin.Plugin.MetaShark.Test { @@ -30,21 +32,26 @@ namespace Jellyfin.Plugin.MetaShark.Test [TestMethod] public void TestGuessEpisodeNumber() { - var doubanApi = new DoubanApi(loggerFactory); - var tmdbApi = new TmdbApi(loggerFactory); - var omdbApi = new OmdbApi(loggerFactory); var httpClientFactory = new DefaultHttpClientFactory(); var libraryManagerStub = new Mock(); + var doubanApi = new DoubanApi(loggerFactory); + var tmdbApi = new TmdbApi(loggerFactory); + var omdbApi = new OmdbApi(loggerFactory); + var provider = new EpisodeProvider(httpClientFactory, loggerFactory, libraryManagerStub.Object, doubanApi, tmdbApi, omdbApi); - var indexNumber = provider.GuessEpisodeNumber("[POPGO][Stand_Alone_Complex][05][1080P][BluRay][x264_FLACx2_AC3x1][chs_jpn][D87C36B6].mkv"); - Assert.AreEqual(indexNumber, 5); + var guessInfo = provider.GuessEpisodeNumber("[POPGO][Stand_Alone_Complex][05][1080P][BluRay][x264_FLACx2_AC3x1][chs_jpn][D87C36B6].mkv"); + Assert.AreEqual(guessInfo.episodeNumber, 5); - indexNumber = provider.GuessEpisodeNumber("Fullmetal Alchemist Brotherhood.E05.1920X1080"); - Assert.AreEqual(indexNumber, 5); + guessInfo = provider.GuessEpisodeNumber("Fullmetal Alchemist Brotherhood.E05.1920X1080"); + Assert.AreEqual(guessInfo.episodeNumber, 5); - indexNumber = provider.GuessEpisodeNumber("[SAIO-Raws] Neon Genesis Evangelion 05 [BD 1440x1080 HEVC-10bit OPUSx2 ASSx2].mkv"); - Assert.AreEqual(indexNumber, 5); + guessInfo = provider.GuessEpisodeNumber("[SAIO-Raws] Neon Genesis Evangelion 05 [BD 1440x1080 HEVC-10bit OPUSx2 ASSx2].mkv"); + Assert.AreEqual(guessInfo.episodeNumber, 5); + + guessInfo = provider.GuessEpisodeNumber("[Moozzi2] Samurai Champloo [SP03] Battlecry (Opening) PV (BD 1920x1080 x.264 AC3).mkv"); + Assert.AreEqual(guessInfo.episodeNumber, 3); + Assert.AreEqual(guessInfo.seasonNumber, 0); } } diff --git a/Jellyfin.Plugin.MetaShark/Api/DoubanApi.cs b/Jellyfin.Plugin.MetaShark/Api/DoubanApi.cs index 13a3ff4..7e6c7b2 100644 --- a/Jellyfin.Plugin.MetaShark/Api/DoubanApi.cs +++ b/Jellyfin.Plugin.MetaShark/Api/DoubanApi.cs @@ -94,7 +94,7 @@ namespace Jellyfin.Plugin.MetaShark.Api private void SetDoubanCookie(CookieContainer cookieContainer) { - var configCookie = Plugin.Instance!.Configuration.DoubanCookies.Trim(); + var configCookie = Plugin.Instance?.Configuration.DoubanCookies.Trim() ?? string.Empty; if (string.IsNullOrEmpty(configCookie)) { return; diff --git a/Jellyfin.Plugin.MetaShark/Api/TmdbApi.cs b/Jellyfin.Plugin.MetaShark/Api/TmdbApi.cs index 8ef0154..1cf4d68 100644 --- a/Jellyfin.Plugin.MetaShark/Api/TmdbApi.cs +++ b/Jellyfin.Plugin.MetaShark/Api/TmdbApi.cs @@ -35,7 +35,8 @@ namespace Jellyfin.Plugin.MetaShark.Api { _logger = loggerFactory.CreateLogger(); _memoryCache = new MemoryCache(new MemoryCacheOptions()); - var apiKey = string.IsNullOrEmpty(Plugin.Instance!.Configuration.TmdbApiKey) ? DEFAULT_API_KEY : Plugin.Instance!.Configuration.TmdbApiKey; + var config = Plugin.Instance?.Configuration; + var apiKey = config == null || string.IsNullOrEmpty(config.TmdbApiKey) ? DEFAULT_API_KEY : config.TmdbApiKey; _tmDbClient = new TMDbClient(apiKey); _tmDbClient.RequestTimeout = TimeSpan.FromSeconds(10); // Not really interested in NotFoundException @@ -675,7 +676,7 @@ namespace Jellyfin.Plugin.MetaShark.Api private bool IsEnable() { - return Plugin.Instance!.Configuration.EnableTmdb; + return Plugin.Instance?.Configuration.EnableTmdb ?? false; } } diff --git a/Jellyfin.Plugin.MetaShark/Model/GuessInfo.cs b/Jellyfin.Plugin.MetaShark/Model/GuessInfo.cs new file mode 100644 index 0000000..964c9f6 --- /dev/null +++ b/Jellyfin.Plugin.MetaShark/Model/GuessInfo.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Jellyfin.Plugin.MetaShark.Model +{ + public class GuessInfo + { + public int? episodeNumber { get; set; } + + public int? seasonNumber { get; set; } + } +} \ No newline at end of file diff --git a/Jellyfin.Plugin.MetaShark/Providers/BaseProvider.cs b/Jellyfin.Plugin.MetaShark/Providers/BaseProvider.cs index 34c7a8e..adea498 100644 --- a/Jellyfin.Plugin.MetaShark/Providers/BaseProvider.cs +++ b/Jellyfin.Plugin.MetaShark/Providers/BaseProvider.cs @@ -18,6 +18,7 @@ using System.Threading; using System.Threading.Tasks; using System.Web; using TMDbLib.Objects.General; +using Jellyfin.Plugin.MetaShark.Configuration; namespace Jellyfin.Plugin.MetaShark.Providers { @@ -51,7 +52,15 @@ namespace Jellyfin.Plugin.MetaShark.Providers { get { - return Plugin.Instance!.Configuration.Pattern; + return this.config.Pattern; + } + } + + protected PluginConfiguration config + { + get + { + return Plugin.Instance?.Configuration ?? new PluginConfiguration(); } } diff --git a/Jellyfin.Plugin.MetaShark/Providers/EpisodeProvider.cs b/Jellyfin.Plugin.MetaShark/Providers/EpisodeProvider.cs index ac04833..26e005c 100644 --- a/Jellyfin.Plugin.MetaShark/Providers/EpisodeProvider.cs +++ b/Jellyfin.Plugin.MetaShark/Providers/EpisodeProvider.cs @@ -35,7 +35,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers }; public EpisodeProvider(IHttpClientFactory httpClientFactory, ILoggerFactory loggerFactory, ILibraryManager libraryManager, DoubanApi doubanApi, TmdbApi tmdbApi, OmdbApi omdbApi) - : base(httpClientFactory, loggerFactory.CreateLogger(), libraryManager, doubanApi, tmdbApi, omdbApi) + : base(httpClientFactory, loggerFactory.CreateLogger(), libraryManager, doubanApi, tmdbApi, omdbApi) { } @@ -70,18 +70,22 @@ namespace Jellyfin.Plugin.MetaShark.Providers this.Log("FixSeasionNumber: old: {0} new: {1}", seasonNumber, season.IndexNumber); seasonNumber = season.IndexNumber; } - // 没有season级目录时,会为null + // 没有season级目录或目录不命名不规范时,会为null if (seasonNumber is null or 0) { seasonNumber = 1; } // 修正anime命名格式导致的episodeNumber错误 var fileName = Path.GetFileName(info.Path) ?? string.Empty; - var newEpisodeNumber = this.GuessEpisodeNumber(fileName); - this.Log("GuessEpisodeNumber: fileName: {0} episodeNumber: {1}", fileName, newEpisodeNumber); - if (newEpisodeNumber.HasValue && newEpisodeNumber != episodeNumber) + var guessInfo = this.GuessEpisodeNumber(fileName); + this.Log("GuessEpisodeNumber: fileName: {0} seasonNumber: {1} episodeNumber: {2}", fileName, guessInfo.seasonNumber, guessInfo.episodeNumber); + if (guessInfo.seasonNumber.HasValue && guessInfo.seasonNumber != seasonNumber) { - episodeNumber = newEpisodeNumber; + seasonNumber = guessInfo.seasonNumber.Value; + } + if (guessInfo.episodeNumber.HasValue && guessInfo.episodeNumber != episodeNumber) + { + episodeNumber = guessInfo.episodeNumber; result.HasMetadata = true; result.Item = new Episode @@ -146,17 +150,23 @@ namespace Jellyfin.Plugin.MetaShark.Providers return _httpClientFactory.CreateClient().GetAsync(new Uri(url), cancellationToken); } - public int? GuessEpisodeNumber(string fileName, double max = double.PositiveInfinity) + public GuessInfo GuessEpisodeNumber(string fileName, double max = double.PositiveInfinity) { - int? episodeIndex = null; + var guessInfo = new GuessInfo(); - var result = AnitomySharp.AnitomySharp.Parse(fileName).FirstOrDefault(x => x.Category == AnitomySharp.Element.ElementCategory.ElementEpisodeNumber); - if (result != null) + var parseResult = AnitomySharp.AnitomySharp.Parse(fileName); + var animeSpecialType = parseResult.FirstOrDefault(x => x.Category == AnitomySharp.Element.ElementCategory.ElementAnimeType && x.Value == "SP"); + if (animeSpecialType != null) { - episodeIndex = result.Value.ToInt(); + guessInfo.seasonNumber = 0; + } + var animeEpisode = parseResult.FirstOrDefault(x => x.Category == AnitomySharp.Element.ElementCategory.ElementEpisodeNumber); + if (animeEpisode != null) + { + guessInfo.episodeNumber = animeEpisode.Value.ToInt(); } - if (!episodeIndex.HasValue) + if (!guessInfo.episodeNumber.HasValue) { foreach (var regex in EpisodeFileNameRegex) { @@ -164,18 +174,18 @@ namespace Jellyfin.Plugin.MetaShark.Providers continue; if (!int.TryParse(regex.Match(fileName).Groups[1].Value.Trim('.'), out var index)) continue; - episodeIndex = index; + guessInfo.episodeNumber = index; break; } } - if (episodeIndex > 1000) + if (guessInfo.episodeNumber > 1000) { // 可能解析了分辨率,忽略返回 - episodeIndex = null; + guessInfo.episodeNumber = null; } - return episodeIndex; + return guessInfo; } } diff --git a/Jellyfin.Plugin.MetaShark/Providers/MovieProvider.cs b/Jellyfin.Plugin.MetaShark/Providers/MovieProvider.cs index d6947d6..783de92 100644 --- a/Jellyfin.Plugin.MetaShark/Providers/MovieProvider.cs +++ b/Jellyfin.Plugin.MetaShark/Providers/MovieProvider.cs @@ -51,7 +51,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers // 从douban搜索 var res = await this._doubanApi.SearchAsync(info.Name, cancellationToken).ConfigureAwait(false); - result.AddRange(res.Take(Plugin.Instance!.Configuration.MaxSearchResult).Select(x => + result.AddRange(res.Take(this.config.MaxSearchResult).Select(x => { return new RemoteSearchResult { @@ -68,7 +68,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers if (Plugin.Instance?.Configuration.EnableTmdbSearch ?? false) { var tmdbList = await _tmdbApi.SearchMovieAsync(info.Name, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); - result.AddRange(tmdbList.Take(Plugin.Instance!.Configuration.MaxSearchResult).Select(x => + result.AddRange(tmdbList.Take(this.config.MaxSearchResult).Select(x => { return new RemoteSearchResult { @@ -143,7 +143,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers result.Item = movie; result.QueriedById = true; result.HasMetadata = true; - subject.LimitDirectorCelebrities.Take(Plugin.Instance!.Configuration.MaxCastMembers).ToList().ForEach(c => result.AddPerson(new PersonInfo + subject.LimitDirectorCelebrities.Take(this.config.MaxCastMembers).ToList().ForEach(c => result.AddPerson(new PersonInfo { Name = c.Name, Type = c.RoleType, @@ -220,7 +220,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers // 演员 if (item.Credits?.Cast != null) { - foreach (var actor in item.Credits.Cast.OrderBy(a => a.Order).Take(Plugin.Instance!.Configuration.MaxCastMembers)) + foreach (var actor in item.Credits.Cast.OrderBy(a => a.Order).Take(this.config.MaxCastMembers)) { var personInfo = new PersonInfo { diff --git a/Jellyfin.Plugin.MetaShark/Providers/SeasonProvider.cs b/Jellyfin.Plugin.MetaShark/Providers/SeasonProvider.cs index 5c17532..0e8bb5b 100644 --- a/Jellyfin.Plugin.MetaShark/Providers/SeasonProvider.cs +++ b/Jellyfin.Plugin.MetaShark/Providers/SeasonProvider.cs @@ -105,7 +105,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers result.Item = movie; result.HasMetadata = true; - subject.LimitDirectorCelebrities.Take(Plugin.Instance!.Configuration.MaxCastMembers).ToList().ForEach(c => result.AddPerson(new PersonInfo + subject.LimitDirectorCelebrities.Take(this.config.MaxCastMembers).ToList().ForEach(c => result.AddPerson(new PersonInfo { Name = c.Name, Type = c.RoleType, @@ -200,7 +200,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers // result.Item = movie; // result.HasMetadata = true; - // subject.Celebrities.Take(Plugin.Instance!.Configuration.MaxCastMembers).ToList().ForEach(c => result.AddPerson(new PersonInfo + // subject.Celebrities.Take(this.config.MaxCastMembers).ToList().ForEach(c => result.AddPerson(new PersonInfo // { // Name = c.Name, // Type = c.RoleType, @@ -222,7 +222,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers // 演员 if (item.Credits?.Cast != null) { - foreach (var actor in item.Credits.Cast.OrderBy(a => a.Order).Take(Plugin.Instance!.Configuration.MaxCastMembers)) + foreach (var actor in item.Credits.Cast.OrderBy(a => a.Order).Take(this.config.MaxCastMembers)) { var personInfo = new PersonInfo { diff --git a/Jellyfin.Plugin.MetaShark/Providers/SeriesProvider.cs b/Jellyfin.Plugin.MetaShark/Providers/SeriesProvider.cs index 34851c5..6177b7f 100644 --- a/Jellyfin.Plugin.MetaShark/Providers/SeriesProvider.cs +++ b/Jellyfin.Plugin.MetaShark/Providers/SeriesProvider.cs @@ -44,7 +44,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers // 从douban搜索 var res = await this._doubanApi.SearchAsync(info.Name, cancellationToken).ConfigureAwait(false); - result.AddRange(res.Take(Plugin.Instance!.Configuration.MaxSearchResult).Select(x => + result.AddRange(res.Take(this.config.MaxSearchResult).Select(x => { return new RemoteSearchResult { @@ -60,7 +60,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers if (Plugin.Instance?.Configuration.EnableTmdbSearch ?? false) { var tmdbList = await this._tmdbApi.SearchSeriesAsync(info.Name, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); - result.AddRange(tmdbList.Take(Plugin.Instance!.Configuration.MaxSearchResult).Select(x => + result.AddRange(tmdbList.Take(this.config.MaxSearchResult).Select(x => { return new RemoteSearchResult { @@ -142,7 +142,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers result.Item = item; result.QueriedById = true; result.HasMetadata = true; - subject.LimitDirectorCelebrities.Take(Plugin.Instance!.Configuration.MaxCastMembers).ToList().ForEach(c => result.AddPerson(new PersonInfo + subject.LimitDirectorCelebrities.Take(this.config.MaxCastMembers).ToList().ForEach(c => result.AddPerson(new PersonInfo { Name = c.Name, Type = c.RoleType, @@ -279,7 +279,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers // 演员 if (seriesResult.Credits?.Cast != null) { - foreach (var actor in seriesResult.Credits.Cast.OrderBy(a => a.Order).Take(Plugin.Instance!.Configuration.MaxCastMembers)) + foreach (var actor in seriesResult.Credits.Cast.OrderBy(a => a.Order).Take(this.config.MaxCastMembers)) { var personInfo = new PersonInfo { diff --git a/README.md b/README.md index fa82a66..f36f198 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![metashark](https://img.shields.io/badge/jellyfin-10.8.x-lightgrey)](https://github.com/cxfksword/jellyfin-plugin-metashark/releases) [![metashark](https://img.shields.io/github/license/cxfksword/jellyfin-plugin-metashark)](https://github.com/cxfksword/jellyfin-plugin-metashark/main/LICENSE) -jellyfin电影元数据插件,影片信息只要从豆瓣获取,并由TheMovieDb补充缺失的季数据和剧集数据。 +jellyfin电影元数据插件,影片信息只要从豆瓣获取,并由TheMovieDb补充缺失的剧集数据。 功能: * 支持从豆瓣和TMDB获取元数据