From b01852ff0a950b0ff4e49feb4b3d991ca9e9e449 Mon Sep 17 00:00:00 2001 From: Qstick Date: Sun, 23 Apr 2023 18:11:43 -0500 Subject: [PATCH] New: Use languages from Torznab/Newznab attributes if given (cherry picked from commit 9c5a07f62a6e32832c10c80813cd3b98c5859989) Fixes #3701 --- src/NzbDrone.Core/Indexers/IndexerBase.cs | 4 ++ .../Indexers/Newznab/NewznabRssParser.cs | 43 +++++++++++++++++++ src/NzbDrone.Core/Indexers/RssParser.cs | 7 +++ .../Indexers/Torznab/TorznabRssParser.cs | 43 +++++++++++++++++++ src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs | 9 ++++ 5 files changed, 106 insertions(+) diff --git a/src/NzbDrone.Core/Indexers/IndexerBase.cs b/src/NzbDrone.Core/Indexers/IndexerBase.cs index ebaa84512..333bada23 100644 --- a/src/NzbDrone.Core/Indexers/IndexerBase.cs +++ b/src/NzbDrone.Core/Indexers/IndexerBase.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; using FluentValidation.Results; using NLog; using NzbDrone.Common.Http; @@ -15,6 +16,8 @@ namespace NzbDrone.Core.Indexers public abstract class IndexerBase : IIndexer where TSettings : IIndexerSettings, new() { + private static readonly Regex MultiRegex = new (@"\b(?multi)\b", RegexOptions.Compiled | RegexOptions.IgnoreCase); + protected readonly IIndexerStatusService _indexerStatusService; protected readonly IConfigService _configService; protected readonly IParsingService _parsingService; @@ -75,6 +78,7 @@ namespace NzbDrone.Core.Indexers protected virtual IList CleanupReleases(IEnumerable releases) { var result = releases.DistinctBy(v => v.Guid).ToList(); + var settings = Definition.Settings as IIndexerSettings; result.ForEach(c => { diff --git a/src/NzbDrone.Core/Indexers/Newznab/NewznabRssParser.cs b/src/NzbDrone.Core/Indexers/Newznab/NewznabRssParser.cs index 24a203438..1dedb89ad 100644 --- a/src/NzbDrone.Core/Indexers/Newznab/NewznabRssParser.cs +++ b/src/NzbDrone.Core/Indexers/Newznab/NewznabRssParser.cs @@ -4,6 +4,8 @@ using System.Linq; using System.Xml.Linq; using NzbDrone.Common.Extensions; using NzbDrone.Core.Indexers.Exceptions; +using NzbDrone.Core.Languages; +using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Indexers.Newznab @@ -101,6 +103,30 @@ namespace NzbDrone.Core.Indexers.Newznab return ParseUrl(item.TryGetValue("comments")); } + protected override List GetLanguages(XElement item) + { + var languges = TryGetMultipleNewznabAttributes(item, "language"); + var results = new List(); + + // Try to find elements for some indexers that suck at following the rules. + if (languges.Count == 0) + { + languges = item.Elements("language").Select(e => e.Value).ToList(); + } + + foreach (var language in languges) + { + var mappedLanguage = IsoLanguages.FindByName(language)?.Language ?? null; + + if (mappedLanguage != null) + { + results.Add(mappedLanguage); + } + } + + return results; + } + protected override long GetSize(XElement item) { long size; @@ -165,5 +191,22 @@ namespace NzbDrone.Core.Indexers.Newznab return defaultValue; } + + protected List TryGetMultipleNewznabAttributes(XElement item, string key) + { + var attrElements = item.Elements(ns + "attr").Where(e => e.Attribute("name").Value.Equals(key, StringComparison.OrdinalIgnoreCase)); + var results = new List(); + + foreach (var element in attrElements) + { + var attrValue = element.Attribute("value"); + if (attrValue != null) + { + results.Add(attrValue.Value); + } + } + + return results; + } } } diff --git a/src/NzbDrone.Core/Indexers/RssParser.cs b/src/NzbDrone.Core/Indexers/RssParser.cs index 1d7f1794e..9491365e3 100644 --- a/src/NzbDrone.Core/Indexers/RssParser.cs +++ b/src/NzbDrone.Core/Indexers/RssParser.cs @@ -12,6 +12,7 @@ using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Common.Instrumentation; using NzbDrone.Core.Indexers.Exceptions; +using NzbDrone.Core.Languages; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Indexers @@ -158,6 +159,7 @@ namespace NzbDrone.Core.Indexers releaseInfo.DownloadUrl = GetDownloadUrl(item); releaseInfo.InfoUrl = GetInfoUrl(item); releaseInfo.CommentUrl = GetCommentUrl(item); + releaseInfo.Languages = GetLanguages(item); try { @@ -224,6 +226,11 @@ namespace NzbDrone.Core.Indexers return ParseUrl((string)item.Element("comments")); } + protected virtual List GetLanguages(XElement item) + { + return new List(); + } + protected virtual long GetSize(XElement item) { if (UseEnclosureLength) diff --git a/src/NzbDrone.Core/Indexers/Torznab/TorznabRssParser.cs b/src/NzbDrone.Core/Indexers/Torznab/TorznabRssParser.cs index 82bcd155d..74f8c1982 100644 --- a/src/NzbDrone.Core/Indexers/Torznab/TorznabRssParser.cs +++ b/src/NzbDrone.Core/Indexers/Torznab/TorznabRssParser.cs @@ -4,6 +4,8 @@ using System.Linq; using System.Xml.Linq; using NzbDrone.Common.Extensions; using NzbDrone.Core.Indexers.Exceptions; +using NzbDrone.Core.Languages; +using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Indexers.Torznab @@ -82,6 +84,30 @@ namespace NzbDrone.Core.Indexers.Torznab return ParseUrl(item.TryGetValue("comments")); } + protected override List GetLanguages(XElement item) + { + var languges = TryGetMultipleTorznabAttributes(item, "language"); + var results = new List(); + + // Try to find elements for some indexers that suck at following the rules. + if (languges.Count == 0) + { + languges = item.Elements("language").Select(e => e.Value).ToList(); + } + + foreach (var language in languges) + { + var mappedLanguage = IsoLanguages.FindByName(language)?.Language ?? null; + + if (mappedLanguage != null) + { + results.Add(mappedLanguage); + } + } + + return results; + } + protected override long GetSize(XElement item) { long size; @@ -170,5 +196,22 @@ namespace NzbDrone.Core.Indexers.Torznab return defaultValue; } + + protected List TryGetMultipleTorznabAttributes(XElement item, string key) + { + var attrElements = item.Elements(ns + "attr").Where(e => e.Attribute("name").Value.Equals(key, StringComparison.OrdinalIgnoreCase)); + var results = new List(); + + foreach (var element in attrElements) + { + var attrValue = element.Attribute("value"); + if (attrValue != null) + { + results.Add(attrValue.Value); + } + } + + return results; + } } } diff --git a/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs b/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs index fbd5f34b8..08ee9284c 100644 --- a/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs +++ b/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs @@ -1,13 +1,20 @@ using System; +using System.Collections.Generic; using System.Text; using Newtonsoft.Json; using NzbDrone.Core.Download.Pending; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Languages; namespace NzbDrone.Core.Parser.Model { public class ReleaseInfo { + public ReleaseInfo() + { + Languages = new List(); + } + public string Guid { get; set; } public string Title { get; set; } public long Size { get; set; } @@ -28,6 +35,8 @@ namespace NzbDrone.Core.Parser.Model public string Codec { get; set; } public string Resolution { get; set; } + public List Languages { get; set; } + // Used to track pending releases that are being reprocessed [JsonIgnore] public PendingReleaseReason? PendingReleaseReason { get; set; }