From b2ed15d15441d24b41c44f402b9b5db2e82eee87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20F=C3=BCrni=C3=9F?= Date: Sun, 13 Mar 2022 22:12:41 +0100 Subject: [PATCH 1/2] Fix episode metadata & improve series metadata --- .../Configuration/PluginConfiguration.cs | 2 + .../Configuration/configPage.html | 9 + .../AniDB/Converter/AniDbEpisodeIdentity.cs | 50 ----- .../AniDB/Converter/TvdbEpisodeIdentity.cs | 71 ------- .../AniDB/Metadata/AniDbEpisodeProvider.cs | 174 +++++------------- .../AniDB/Metadata/AniDbSeriesProvider.cs | 11 +- 6 files changed, 64 insertions(+), 253 deletions(-) delete mode 100644 Jellyfin.Plugin.AniDB/Providers/AniDB/Converter/AniDbEpisodeIdentity.cs delete mode 100644 Jellyfin.Plugin.AniDB/Providers/AniDB/Converter/TvdbEpisodeIdentity.cs diff --git a/Jellyfin.Plugin.AniDB/Configuration/PluginConfiguration.cs b/Jellyfin.Plugin.AniDB/Configuration/PluginConfiguration.cs index 26bb230..3cf9ac0 100644 --- a/Jellyfin.Plugin.AniDB/Configuration/PluginConfiguration.cs +++ b/Jellyfin.Plugin.AniDB/Configuration/PluginConfiguration.cs @@ -43,6 +43,8 @@ namespace Jellyfin.Plugin.AniDB.Configuration public TitlePreferenceType OriginalTitlePreference { get; set; } + public bool IgnoreSeason { get; set; } + public int MaxGenres { get; set; } public bool TidyGenreList { get; set; } diff --git a/Jellyfin.Plugin.AniDB/Configuration/configPage.html b/Jellyfin.Plugin.AniDB/Configuration/configPage.html index d5cbb62..eda3201 100644 --- a/Jellyfin.Plugin.AniDB/Configuration/configPage.html +++ b/Jellyfin.Plugin.AniDB/Configuration/configPage.html @@ -26,6 +26,13 @@ +
+ +
AniDB doesn't support seasons. If checked, it will treat every season like season one, except for specials.
+
@@ -81,6 +88,7 @@ ApiClient.getPluginConfiguration(AniDBConfigurationPage.pluginUniqueId).then(function (config) { document.getElementById('titleLanguage').value = config.TitlePreference; document.getElementById('originalTitleLanguage').value = config.OriginalTitlePreference; + document.getElementById('chkIgnoreSeason').checked = config.IgnoreSeason; document.getElementById('chkMaxGenres').value = config.MaxGenres; document.getElementById('chkTitleCaseGenres').checked = config.TitleCaseGenres; document.getElementById('chkTidyGenres').checked = config.TidyGenreList; @@ -98,6 +106,7 @@ ApiClient.getPluginConfiguration(AniDBConfigurationPage.pluginUniqueId).then(function (config) { config.TitlePreference = document.getElementById('titleLanguage').value; config.OriginalTitlePreference = document.getElementById('originalTitleLanguage').value; + config.IgnoreSeason = document.getElementById('chkIgnoreSeason').checked; config.MaxGenres = document.getElementById('chkMaxGenres').value; config.TitleCaseGenres = document.getElementById('chkTitleCaseGenres').checked; config.TidyGenreList = document.getElementById('chkTidyGenres').checked; diff --git a/Jellyfin.Plugin.AniDB/Providers/AniDB/Converter/AniDbEpisodeIdentity.cs b/Jellyfin.Plugin.AniDB/Providers/AniDB/Converter/AniDbEpisodeIdentity.cs deleted file mode 100644 index 8a4ccd1..0000000 --- a/Jellyfin.Plugin.AniDB/Providers/AniDB/Converter/AniDbEpisodeIdentity.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.Text.RegularExpressions; - -namespace Jellyfin.Plugin.AniDB.Providers.AniDB.Converter -{ - public struct AniDbEpisodeIdentity - { - private static readonly Regex _regex = new Regex(@"(?\d+):(?[S])?(?\d+)(-(?\d+))?"); - - public AniDbEpisodeIdentity(string id) - { - this = Parse(id).Value; - } - - public AniDbEpisodeIdentity(string seriesId, int episodeNumber, int? episodeNumberEnd, string episodeType) - { - SeriesId = seriesId; - EpisodeNumber = episodeNumber; - EpisodeNumberEnd = episodeNumberEnd; - EpisodeType = episodeType; - } - - public string SeriesId { get; private set; } - public int EpisodeNumber { get; private set; } - public int? EpisodeNumberEnd { get; private set; } - public string EpisodeType { get; private set; } - - public override string ToString() - { - return string.Format("{0}:{1}{2}", - SeriesId, - EpisodeType ?? "", - EpisodeNumber + (EpisodeNumberEnd != null ? "-" + EpisodeNumberEnd.Value.ToString() : "")); - } - - public static AniDbEpisodeIdentity? Parse(string id) - { - var match = _regex.Match(id); - if (match.Success) - { - return new AniDbEpisodeIdentity( - match.Groups["series"].Value, - int.Parse(match.Groups["epno"].Value), - match.Groups["epnoend"].Success ? int.Parse(match.Groups["epnoend"].Value) : (int?)null, - match.Groups["type"].Success ? match.Groups["type"].Value : null); - } - - return null; - } - } -} diff --git a/Jellyfin.Plugin.AniDB/Providers/AniDB/Converter/TvdbEpisodeIdentity.cs b/Jellyfin.Plugin.AniDB/Providers/AniDB/Converter/TvdbEpisodeIdentity.cs deleted file mode 100644 index dbb51e0..0000000 --- a/Jellyfin.Plugin.AniDB/Providers/AniDB/Converter/TvdbEpisodeIdentity.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; - -namespace Jellyfin.Plugin.AniDB.Providers.AniDB.Converter -{ - public struct TvdbEpisodeIdentity - { - public TvdbEpisodeIdentity(string id) - : this() - { - this = Parse(id).Value; - } - - public TvdbEpisodeIdentity(string seriesId, int? seasonIndex, int episodeNumber, int? episodeNumberEnd) - : this() - { - SeriesId = seriesId; - SeasonIndex = seasonIndex; - EpisodeNumber = episodeNumber; - EpisodeNumberEnd = episodeNumberEnd; - } - - public string SeriesId { get; private set; } - public int? SeasonIndex { get; private set; } - public int EpisodeNumber { get; private set; } - public int? EpisodeNumberEnd { get; private set; } - - public override string ToString() - { - return string.Format("{0}:{1}:{2}", - SeriesId, - SeasonIndex != null ? SeasonIndex.Value.ToString() : "A", - EpisodeNumber + (EpisodeNumberEnd != null ? "-" + EpisodeNumberEnd.Value.ToString() : "")); - } - - public static TvdbEpisodeIdentity? Parse(string id) - { - if (string.IsNullOrEmpty(id)) - { - return null; - } - - try - { - var parts = id.Split(':'); - var series = parts[0]; - var season = parts[1] != "A" ? (int?)int.Parse(parts[1]) : null; - - int index; - int? indexEnd; - - var split = parts[2].IndexOf("-", StringComparison.OrdinalIgnoreCase); - if (split != -1) - { - index = int.Parse(parts[2].Substring(0, split)); - indexEnd = int.Parse(parts[2].Substring(split + 1)); - } - else - { - index = int.Parse(parts[2]); - indexEnd = null; - } - - return new TvdbEpisodeIdentity(series, season, index, indexEnd); - } - catch - { - return null; - } - } - } -} diff --git a/Jellyfin.Plugin.AniDB/Providers/AniDB/Metadata/AniDbEpisodeProvider.cs b/Jellyfin.Plugin.AniDB/Providers/AniDB/Metadata/AniDbEpisodeProvider.cs index b79149b..5a4411d 100644 --- a/Jellyfin.Plugin.AniDB/Providers/AniDB/Metadata/AniDbEpisodeProvider.cs +++ b/Jellyfin.Plugin.AniDB/Providers/AniDB/Metadata/AniDbEpisodeProvider.cs @@ -6,8 +6,6 @@ using System.IO; using System.Threading; using System.Threading.Tasks; using System.Xml; -using Jellyfin.Plugin.AniDB.Providers.AniDB.Converter; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; @@ -32,30 +30,38 @@ namespace Jellyfin.Plugin.AniDB.Providers.AniDB.Metadata _configurationManager = configurationManager; } + public string Name => "AniDB"; + public async Task> GetMetadata(EpisodeInfo info, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var result = new MetadataResult(); - var aniDbId = info.ProviderIds.GetOrDefault(ProviderNames.AniDb); - if (string.IsNullOrEmpty(aniDbId)) + var animeId = info.SeriesProviderIds.GetOrDefault(ProviderNames.AniDb); + if (string.IsNullOrEmpty(animeId)) { return result; } - var id = AniDbEpisodeIdentity.Parse(aniDbId); - if (id == null) - { - return result; - } - - var seriesFolder = await FindSeriesFolder(id.Value.SeriesId, cancellationToken); + var seriesFolder = await FindSeriesFolder(animeId, cancellationToken); if (string.IsNullOrEmpty(seriesFolder)) { return result; } - var xml = GetEpisodeXmlFile(id.Value.EpisodeNumber, id.Value.EpisodeType, seriesFolder); + if (!Plugin.Instance.Configuration.IgnoreSeason && info.ParentIndexNumber > 1) + { + return result; + } + + string episodeType = ""; + + if (info.ParentIndexNumber == 0) + { + episodeType = "S"; + } + + var xml = GetEpisodeXmlFile(info.IndexNumber, episodeType, seriesFolder); if (xml == null || !xml.Exists) { return result; @@ -71,71 +77,39 @@ namespace Jellyfin.Plugin.AniDB.Providers.AniDB.Metadata await ParseEpisodeXml(xml, result.Item, info.MetadataLanguage).ConfigureAwait(false); - if (id.Value.EpisodeNumberEnd != null && id.Value.EpisodeNumberEnd > id.Value.EpisodeNumber) - { - for (var i = id.Value.EpisodeNumber + 1; i <= id.Value.EpisodeNumberEnd; i++) - { - var additionalXml = GetEpisodeXmlFile(i, id.Value.EpisodeType, seriesFolder); - if (additionalXml == null || !additionalXml.Exists) - { - continue; - } - - await ParseAdditionalEpisodeXml(additionalXml, result.Item, info.MetadataLanguage).ConfigureAwait(false); - } - } - return result; } - public string Name => "AniDB"; - public async Task> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken) { - var list = new List(); - - var id = AniDbEpisodeIdentity.Parse(searchInfo.ProviderIds.GetOrDefault(ProviderNames.AniDb)); - if (id == null) + if (!searchInfo.IndexNumber.HasValue || !searchInfo.ParentIndexNumber.HasValue) { - return list; + return new List(); } - await AniDbSeriesProvider.GetSeriesData( - _configurationManager.ApplicationPaths, - id.Value.SeriesId, - cancellationToken).ConfigureAwait(false); + var metadataResult = await GetMetadata(searchInfo, cancellationToken).ConfigureAwait(false); - try + if (!metadataResult.HasMetadata) { - var metadataResult = await GetMetadata(searchInfo, cancellationToken).ConfigureAwait(false); + return new List(); + } - if (metadataResult.HasMetadata) + var item = metadataResult.Item; + + return new[] + { + new RemoteSearchResult { - var item = metadataResult.Item; - - list.Add(new RemoteSearchResult - { - IndexNumber = item.IndexNumber, - Name = item.Name, - ParentIndexNumber = item.ParentIndexNumber, - PremiereDate = item.PremiereDate, - ProductionYear = item.ProductionYear, - ProviderIds = item.ProviderIds, - SearchProviderName = Name, - IndexNumberEnd = item.IndexNumberEnd - }); + IndexNumber = item.IndexNumber, + Name = item.Name, + ParentIndexNumber = item.ParentIndexNumber, + PremiereDate = item.PremiereDate, + ProductionYear = item.ProductionYear, + ProviderIds = item.ProviderIds, + SearchProviderName = Name, + IndexNumberEnd = item.IndexNumberEnd } - } - catch (FileNotFoundException) - { - // Don't fail the provider because this will just keep on going and going. - } - catch (DirectoryNotFoundException) - { - // Don't fail the provider because this will just keep on going and going. - } - - return list; + }; } public Task GetImageResponse(string url, CancellationToken cancellationToken) @@ -144,69 +118,6 @@ namespace Jellyfin.Plugin.AniDB.Providers.AniDB.Metadata return imageProvider.GetImageResponse(url, cancellationToken); } - private async Task ParseAdditionalEpisodeXml(FileInfo xml, Episode episode, string metadataLanguage) - { - var settings = new XmlReaderSettings - { - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true, - ValidationType = ValidationType.None - }; - - using (var streamReader = xml.OpenText()) - using (var reader = XmlReader.Create(streamReader, settings)) - { - await reader.MoveToContentAsync().ConfigureAwait(false); - - var titles = new List(); - - while (await reader.ReadAsync().ConfigureAwait(false)) - { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "length": - var length = await reader.ReadElementContentAsStringAsync().ConfigureAwait(false); - if (!string.IsNullOrEmpty(length)) - { - long duration; - if (long.TryParse(length, out duration)) - { - episode.RunTimeTicks += TimeSpan.FromMinutes(duration).Ticks; - } - } - - break; - - case "title": - var language = reader.GetAttribute("xml:lang"); - var name = await reader.ReadElementContentAsStringAsync().ConfigureAwait(false); - - titles.Add(new Title - { - Language = language, - Type = "main", - Name = name - }); - - break; - } - } - } - - var title = titles.Localize(Plugin.Instance.Configuration.TitlePreference, metadataLanguage).Name; - if (!string.IsNullOrEmpty(title)) - { - title = ", " + title; - episode.Name += Plugin.Instance.Configuration.AniDbReplaceGraves - ? title.Replace('`', '\'') - : title; - } - } - } - private async Task<string> FindSeriesFolder(string seriesId, CancellationToken cancellationToken) { var seriesDataPath = await AniDbSeriesProvider.GetSeriesData(_configurationManager.ApplicationPaths, seriesId, cancellationToken).ConfigureAwait(false); @@ -217,6 +128,7 @@ namespace Jellyfin.Plugin.AniDB.Providers.AniDB.Metadata { var settings = new XmlReaderSettings { + Async = true, CheckCharacters = false, IgnoreProcessingInstructions = true, IgnoreComments = true, @@ -284,12 +196,18 @@ namespace Jellyfin.Plugin.AniDB.Providers.AniDB.Metadata Name = name }); + break; + + case "summary": + var overview = AniDbSeriesProvider.ReplaceLineFeedWithNewLine(reader.ReadElementContentAsString()); + episode.Overview = Plugin.Instance.Configuration.AniDbReplaceGraves ? overview.Replace('`', '\'') : overview; + break; } } } - var title = titles.Localize(Plugin.Instance.Configuration.TitlePreference, preferredMetadataLanguage).Name; + var title = titles.Localize(Configuration.TitlePreferenceType.Localized, preferredMetadataLanguage).Name; if (!string.IsNullOrEmpty(title)) { episode.Name = Plugin.Instance.Configuration.AniDbReplaceGraves diff --git a/Jellyfin.Plugin.AniDB/Providers/AniDB/Metadata/AniDbSeriesProvider.cs b/Jellyfin.Plugin.AniDB/Providers/AniDB/Metadata/AniDbSeriesProvider.cs index b2ea011..654551f 100644 --- a/Jellyfin.Plugin.AniDB/Providers/AniDB/Metadata/AniDbSeriesProvider.cs +++ b/Jellyfin.Plugin.AniDB/Providers/AniDB/Metadata/AniDbSeriesProvider.cs @@ -173,6 +173,7 @@ namespace Jellyfin.Plugin.AniDB.Providers.AniDB.Metadata { date = date.ToUniversalTime(); series.PremiereDate = date; + series.ProductionYear = date.Year; } } @@ -221,9 +222,10 @@ namespace Jellyfin.Plugin.AniDB.Providers.AniDB.Metadata break; case "description": - series.Overview = ReplaceLineFeedWithNewLine( - StripAniDbLinks( - await reader.ReadElementContentAsStringAsync().ConfigureAwait(false))); + var description = await reader.ReadElementContentAsStringAsync().ConfigureAwait(false); + description = description.TrimStart('*').Trim(); + series.Overview = ReplaceLineFeedWithNewLine(StripAniDbLinks( + Plugin.Instance.Configuration.AniDbReplaceGraves ? description.Replace('`', '\'') : description)); break; @@ -492,7 +494,8 @@ namespace Jellyfin.Plugin.AniDB.Providers.AniDB.Metadata } else { - series.AddPerson(CreatePerson(name, type)); + series.AddPerson(CreatePerson( + Plugin.Instance.Configuration.AniDbReplaceGraves ? name.Replace('`', '\'') : name, type)); } } } From 87ec7b37dd547b203965dd9fb09219283bd50201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20F=C3=BCrni=C3=9F?= <contact@nalsai.de> Date: Mon, 14 Mar 2022 22:40:00 +0100 Subject: [PATCH 2/2] Fix replacing newlines --- .../Providers/AniDB/Metadata/AniDbEpisodeProvider.cs | 2 +- .../Providers/AniDB/Metadata/AniDbSeriesProvider.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Plugin.AniDB/Providers/AniDB/Metadata/AniDbEpisodeProvider.cs b/Jellyfin.Plugin.AniDB/Providers/AniDB/Metadata/AniDbEpisodeProvider.cs index 5a4411d..aa8383b 100644 --- a/Jellyfin.Plugin.AniDB/Providers/AniDB/Metadata/AniDbEpisodeProvider.cs +++ b/Jellyfin.Plugin.AniDB/Providers/AniDB/Metadata/AniDbEpisodeProvider.cs @@ -199,7 +199,7 @@ namespace Jellyfin.Plugin.AniDB.Providers.AniDB.Metadata break; case "summary": - var overview = AniDbSeriesProvider.ReplaceLineFeedWithNewLine(reader.ReadElementContentAsString()); + var overview = AniDbSeriesProvider.ReplaceNewLine(reader.ReadElementContentAsString()); episode.Overview = Plugin.Instance.Configuration.AniDbReplaceGraves ? overview.Replace('`', '\'') : overview; break; diff --git a/Jellyfin.Plugin.AniDB/Providers/AniDB/Metadata/AniDbSeriesProvider.cs b/Jellyfin.Plugin.AniDB/Providers/AniDB/Metadata/AniDbSeriesProvider.cs index 654551f..82c9090 100644 --- a/Jellyfin.Plugin.AniDB/Providers/AniDB/Metadata/AniDbSeriesProvider.cs +++ b/Jellyfin.Plugin.AniDB/Providers/AniDB/Metadata/AniDbSeriesProvider.cs @@ -224,7 +224,7 @@ namespace Jellyfin.Plugin.AniDB.Providers.AniDB.Metadata case "description": var description = await reader.ReadElementContentAsStringAsync().ConfigureAwait(false); description = description.TrimStart('*').Trim(); - series.Overview = ReplaceLineFeedWithNewLine(StripAniDbLinks( + series.Overview = ReplaceNewLine(StripAniDbLinks( Plugin.Instance.Configuration.AniDbReplaceGraves ? description.Replace('`', '\'') : description)); break; @@ -381,9 +381,9 @@ namespace Jellyfin.Plugin.AniDB.Providers.AniDB.Metadata return AniDbUrlRegex.Replace(text, "${name}"); } - public static string ReplaceLineFeedWithNewLine(string text) + public static string ReplaceNewLine(string text) { - return text.Replace("\n", Environment.NewLine); + return text.Replace("\n", "<br>"); } private async Task ParseActors(MetadataResult<Series> series, XmlReader reader)