diff --git a/src/NzbDrone.Core.Test/Download/Aggregation/Aggregators/AggregateLanguagesFixture.cs b/src/NzbDrone.Core.Test/Download/Aggregation/Aggregators/AggregateLanguagesFixture.cs index 4c9e3b4f3..50d09a154 100644 --- a/src/NzbDrone.Core.Test/Download/Aggregation/Aggregators/AggregateLanguagesFixture.cs +++ b/src/NzbDrone.Core.Test/Download/Aggregation/Aggregators/AggregateLanguagesFixture.cs @@ -4,6 +4,8 @@ using FizzWare.NBuilder; using FluentAssertions; using NUnit.Framework; using NzbDrone.Core.Download.Aggregation.Aggregators; +using NzbDrone.Core.Indexers; +using NzbDrone.Core.Indexers.TorrentRss; using NzbDrone.Core.Languages; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Test.Framework; @@ -62,6 +64,63 @@ namespace NzbDrone.Core.Test.Download.Aggregation.Aggregators Subject.Aggregate(_remoteEpisode).Languages.Should().Equal(_remoteEpisode.ParsedEpisodeInfo.Languages); } + [Test] + public void should_return_multi_languages_when_indexer_has_multi_languages_configuration() + { + var releaseTitle = "Series.Title.S01E01.MULTi.1080p.WEB.H265-RlsGroup"; + var indexerDefinition = new IndexerDefinition + { + Settings = new TorrentRssIndexerSettings { MultiLanguages = new List { Language.Original.Id, Language.French.Id } } + }; + Mocker.GetMock() + .Setup(v => v.Get(1)) + .Returns(indexerDefinition); + + _remoteEpisode.ParsedEpisodeInfo = GetParsedEpisodeInfo(new List { }, releaseTitle); + _remoteEpisode.Release.IndexerId = 1; + _remoteEpisode.Release.Title = releaseTitle; + + Subject.Aggregate(_remoteEpisode).Languages.Should().BeEquivalentTo(new List { _series.OriginalLanguage, Language.French }); + } + + [Test] + public void should_return_multi_languages_when_release_as_unknown_as_default_language_and_indexer_has_multi_languages_configuration() + { + var releaseTitle = "Series.Title.S01E01.MULTi.1080p.WEB.H265-RlsGroup"; + var indexerDefinition = new IndexerDefinition + { + Settings = new TorrentRssIndexerSettings { MultiLanguages = new List { Language.Original.Id, Language.French.Id } } + }; + Mocker.GetMock() + .Setup(v => v.Get(1)) + .Returns(indexerDefinition); + + _remoteEpisode.ParsedEpisodeInfo = GetParsedEpisodeInfo(new List { Language.Unknown }, releaseTitle); + _remoteEpisode.Release.IndexerId = 1; + _remoteEpisode.Release.Title = releaseTitle; + + Subject.Aggregate(_remoteEpisode).Languages.Should().BeEquivalentTo(new List { _series.OriginalLanguage, Language.French }); + } + + [Test] + public void should_return_original_when_indexer_has_no_multi_languages_configuration() + { + var releaseTitle = "Series.Title.S01E01.MULTi.1080p.WEB.H265-RlsGroup"; + var indexerDefinition = new IndexerDefinition + { + Settings = new TorrentRssIndexerSettings { } + }; + Mocker.GetMock() + .Setup(v => v.Get(1)) + .Returns(indexerDefinition); + + _remoteEpisode.ParsedEpisodeInfo = GetParsedEpisodeInfo(new List { }, releaseTitle); + _remoteEpisode.Release.IndexerId = 1; + _remoteEpisode.Release.Title = releaseTitle; + + Subject.Aggregate(_remoteEpisode).Languages.Should().BeEquivalentTo(new List { _series.OriginalLanguage }); + } + [Test] public void should_exclude_language_that_is_part_of_episode_title_when_release_tokens_contains_episode_title() { diff --git a/src/NzbDrone.Core/Download/Aggregation/Aggregators/AggregateLanguages.cs b/src/NzbDrone.Core/Download/Aggregation/Aggregators/AggregateLanguages.cs index b180d9ed7..77c9c9170 100644 --- a/src/NzbDrone.Core/Download/Aggregation/Aggregators/AggregateLanguages.cs +++ b/src/NzbDrone.Core/Download/Aggregation/Aggregators/AggregateLanguages.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; using System.Linq; using NLog; +using NzbDrone.Common.Extensions; +using NzbDrone.Core.Indexers; using NzbDrone.Core.Languages; using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; @@ -10,10 +12,13 @@ namespace NzbDrone.Core.Download.Aggregation.Aggregators { public class AggregateLanguages : IAggregateRemoteEpisode { + private readonly IIndexerFactory _indexerFactory; private readonly Logger _logger; - public AggregateLanguages(Logger logger) + public AggregateLanguages(IIndexerFactory indexerFactory, + Logger logger) { + _indexerFactory = indexerFactory; _logger = logger; } @@ -71,6 +76,17 @@ namespace NzbDrone.Core.Download.Aggregation.Aggregators languages = languages.Except(languagesToRemove).ToList(); } + if ((languages.Count == 0 || (languages.Count == 1 && languages.First() == Language.Unknown)) && releaseInfo is { IndexerId: > 0 } && releaseInfo.Title.IsNotNullOrWhiteSpace()) + { + var indexer = _indexerFactory.Get(releaseInfo.IndexerId); + + if (indexer?.Settings is IIndexerSettings settings && settings.MultiLanguages.Any() && Parser.Parser.HasMultipleLanguages(releaseInfo.Title)) + { + // Use indexer setting for Multi-languages + languages = settings.MultiLanguages.Select(i => (Language)i).ToList(); + } + } + // Use series language as fallback if we couldn't parse a language if (languages.Count == 0 || (languages.Count == 1 && languages.First() == Language.Unknown)) { diff --git a/src/NzbDrone.Core/Indexers/IndexerBase.cs b/src/NzbDrone.Core/Indexers/IndexerBase.cs index 4696bea3c..00a8965c9 100644 --- a/src/NzbDrone.Core/Indexers/IndexerBase.cs +++ b/src/NzbDrone.Core/Indexers/IndexerBase.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text.RegularExpressions; using System.Threading.Tasks; using FluentValidation.Results; using NLog; @@ -20,8 +19,6 @@ namespace NzbDrone.Core.Indexers public abstract class IndexerBase : IIndexer where TSettings : IIndexerSettings, new() { - private static readonly Regex MultiRegex = new (@"[_. ](?multi)[_. ]", RegexOptions.Compiled | RegexOptions.IgnoreCase); - protected readonly IIndexerStatusService _indexerStatusService; protected readonly IConfigService _configService; protected readonly IParsingService _parsingService; @@ -94,7 +91,7 @@ namespace NzbDrone.Core.Indexers result.ForEach(c => { // Use multi languages from setting if ReleaseInfo languages is empty - if (c.Languages.Empty() && MultiRegex.IsMatch(c.Title) && settings.MultiLanguages.Any()) + if (c.Languages.Empty() && settings.MultiLanguages.Any() && Parser.Parser.HasMultipleLanguages(c.Title)) { c.Languages = settings.MultiLanguages.Select(i => (Language)i).ToList(); } diff --git a/src/NzbDrone.Core/Parser/Parser.cs b/src/NzbDrone.Core/Parser/Parser.cs index daad36a6f..54e32470e 100644 --- a/src/NzbDrone.Core/Parser/Parser.cs +++ b/src/NzbDrone.Core/Parser/Parser.cs @@ -575,6 +575,8 @@ namespace NzbDrone.Core.Parser private static readonly string[] Numbers = new[] { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" }; + private static readonly Regex MultiRegex = new (@"[_. ](?multi)[_. ]", RegexOptions.Compiled | RegexOptions.IgnoreCase); + public static ParsedEpisodeInfo ParsePath(string path) { var fileInfo = new FileInfo(path); @@ -959,6 +961,11 @@ namespace NzbDrone.Core.Parser return title; } + public static bool HasMultipleLanguages(string title) + { + return MultiRegex.IsMatch(title); + } + private static SeriesTitleInfo GetSeriesTitleInfo(string title, MatchCollection matchCollection) { var seriesTitleInfo = new SeriesTitleInfo();