diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeFilePreferredWordCalculatorFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeFilePreferredWordCalculatorFixture.cs index 4c42975a5..4ef7d1167 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeFilePreferredWordCalculatorFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeFilePreferredWordCalculatorFixture.cs @@ -1,4 +1,6 @@ +using System.Collections.Generic; using System.IO; +using System.Linq; using FizzWare.NBuilder; using FluentAssertions; using Moq; @@ -15,26 +17,33 @@ namespace NzbDrone.Core.Test.MediaFiles { private Series _series; private EpisodeFile _episodeFile; + private readonly KeyValuePair _positiveScore = new KeyValuePair("Positive", 10); + private readonly KeyValuePair _negativeScore = new KeyValuePair("Negative", -10); + private KeyValuePair _neutralScore = new KeyValuePair("Neutral", 0); [SetUp] public void Setup() { _series = Builder.CreateNew().Build(); _episodeFile = Builder.CreateNew().Build(); + + Mocker.GetMock() + .Setup(s => s.GetMatchingPreferredWordsAndScores(It.IsAny(), It.IsAny(), 0)) + .Returns(new List>()); } - private void GivenPreferredWordScore(string title, int score) + private void GivenPreferredWordScore(string title, params KeyValuePair[] matches) { Mocker.GetMock() - .Setup(s => s.Calculate(It.IsAny(), title, 0)) - .Returns(score); + .Setup(s => s.GetMatchingPreferredWordsAndScores(It.IsAny(), title, 0)) + .Returns(matches.ToList()); } [Test] public void should_return_score_for_relative_file_name_when_it_is_higher_than_scene_name() { - GivenPreferredWordScore(_episodeFile.SceneName, 10); - GivenPreferredWordScore(_episodeFile.RelativePath, 20); + GivenPreferredWordScore(_episodeFile.SceneName, _positiveScore); + GivenPreferredWordScore(_episodeFile.RelativePath, _positiveScore, _positiveScore); Subject.Calculate(_series, _episodeFile).Should().Be(20); } @@ -45,7 +54,7 @@ namespace NzbDrone.Core.Test.MediaFiles _episodeFile.SceneName = null; _episodeFile.RelativePath = null; - GivenPreferredWordScore(_episodeFile.Path, 20); + GivenPreferredWordScore(_episodeFile.Path, _positiveScore, _positiveScore); Subject.Calculate(_series, _episodeFile).Should().Be(20); } @@ -55,7 +64,7 @@ namespace NzbDrone.Core.Test.MediaFiles { _episodeFile.SceneName = null; - GivenPreferredWordScore(_episodeFile.RelativePath, 20); + GivenPreferredWordScore(_episodeFile.RelativePath, _positiveScore, _positiveScore); Subject.Calculate(_series, _episodeFile).Should().Be(20); } @@ -63,17 +72,17 @@ namespace NzbDrone.Core.Test.MediaFiles [Test] public void should_return_score_for_scene_name_when_higher_than_relative_file_name() { - GivenPreferredWordScore(_episodeFile.SceneName, 50); - GivenPreferredWordScore(_episodeFile.RelativePath, 20); + GivenPreferredWordScore(_episodeFile.SceneName, _positiveScore, _positiveScore, _positiveScore); + GivenPreferredWordScore(_episodeFile.RelativePath, _positiveScore, _positiveScore); - Subject.Calculate(_series, _episodeFile).Should().Be(50); + Subject.Calculate(_series, _episodeFile).Should().Be(30); } [Test] public void should_return_score_for_relative_file_if_available() { - GivenPreferredWordScore(_episodeFile.RelativePath, 20); - GivenPreferredWordScore(_episodeFile.Path, 50); + GivenPreferredWordScore(_episodeFile.RelativePath, _positiveScore, _positiveScore); + GivenPreferredWordScore(_episodeFile.Path, _positiveScore, _positiveScore, _positiveScore); Subject.Calculate(_series, _episodeFile).Should().Be(20); } @@ -86,12 +95,12 @@ namespace NzbDrone.Core.Test.MediaFiles _episodeFile.OriginalFilePath = Path.Combine(folderName, fileName); - GivenPreferredWordScore(_episodeFile.RelativePath, 20); - GivenPreferredWordScore(_episodeFile.Path, 50); - GivenPreferredWordScore(folderName, 60); - GivenPreferredWordScore(fileName, 50); + GivenPreferredWordScore(_episodeFile.RelativePath, _positiveScore); + GivenPreferredWordScore(_episodeFile.Path, _positiveScore, _positiveScore); + GivenPreferredWordScore(folderName, _positiveScore, _positiveScore, _positiveScore); + GivenPreferredWordScore(fileName, _positiveScore, _positiveScore); - Subject.Calculate(_series, _episodeFile).Should().Be(60); + Subject.Calculate(_series, _episodeFile).Should().Be(30); } [Test] @@ -102,12 +111,42 @@ namespace NzbDrone.Core.Test.MediaFiles _episodeFile.OriginalFilePath = Path.Combine(folderName, fileName); - GivenPreferredWordScore(_episodeFile.RelativePath, 20); - GivenPreferredWordScore(_episodeFile.Path, 50); - GivenPreferredWordScore(folderName, 40); - GivenPreferredWordScore(fileName, 50); + GivenPreferredWordScore(_episodeFile.RelativePath, _positiveScore); + GivenPreferredWordScore(_episodeFile.Path, _positiveScore, _positiveScore); + GivenPreferredWordScore(folderName, _positiveScore, _positiveScore); + GivenPreferredWordScore(fileName, _positiveScore, _positiveScore, _positiveScore); - Subject.Calculate(_series, _episodeFile).Should().Be(50); + Subject.Calculate(_series, _episodeFile).Should().Be(30); + } + + [Test] + public void should_return_negative_score_if_0_result_has_no_matches() + { + var folderName = "folder-name"; + var fileName = "file-name"; + + _episodeFile.OriginalFilePath = Path.Combine(folderName, fileName); + + GivenPreferredWordScore(_episodeFile.RelativePath, _negativeScore); + GivenPreferredWordScore(fileName); + + Subject.Calculate(_series, _episodeFile).Should().Be(-10); + } + + [Test] + public void should_return_0_score_if_0_result_has_matches() + { + var folderName = "folder-name"; + var fileName = "file-name"; + + _episodeFile.OriginalFilePath = Path.Combine(folderName, fileName); + + GivenPreferredWordScore(_episodeFile.RelativePath, _negativeScore); + GivenPreferredWordScore(_episodeFile.Path, _negativeScore); + GivenPreferredWordScore(folderName, _negativeScore); + GivenPreferredWordScore(fileName, _neutralScore); + + Subject.Calculate(_series, _episodeFile).Should().Be(0); } } } \ No newline at end of file diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeFilePreferredWordCalculator.cs b/src/NzbDrone.Core/MediaFiles/EpisodeFilePreferredWordCalculator.cs index afc2febb8..74e186c6b 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeFilePreferredWordCalculator.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeFilePreferredWordCalculator.cs @@ -32,7 +32,7 @@ namespace NzbDrone.Core.MediaFiles if (episodeFile.SceneName.IsNotNullOrWhiteSpace()) { - scores.Add(_preferredWordService.Calculate(series, episodeFile.SceneName, 0)); + AddScoreIfApplicable(series, episodeFile.SceneName, scores); } else { @@ -49,7 +49,7 @@ namespace NzbDrone.Core.MediaFiles var isLast = i == segments.Count - 1; var segment = isLast ? Path.GetFileNameWithoutExtension(segments[i]) : segments[i]; - scores.Add(_preferredWordService.Calculate(series, segment, 0)); + AddScoreIfApplicable(series, segment, scores); } } else @@ -60,11 +60,11 @@ namespace NzbDrone.Core.MediaFiles // Calculate using RelativePath or Path, but not both if (episodeFile.RelativePath.IsNotNullOrWhiteSpace()) { - scores.Add(_preferredWordService.Calculate(series, episodeFile.RelativePath, 0)); + AddScoreIfApplicable(series, episodeFile.RelativePath, scores); } else if (episodeFile.Path.IsNotNullOrWhiteSpace()) { - scores.Add(_preferredWordService.Calculate(series, episodeFile.Path, 0)); + AddScoreIfApplicable(series, episodeFile.Path, scores); } // Return the highest score, this will allow media info in file names to be used to improve preferred word scoring. @@ -72,5 +72,22 @@ namespace NzbDrone.Core.MediaFiles return scores.MaxOrDefault(); } + + private void AddScoreIfApplicable(Series series, string title, List scores) + { + var score = 0; + _logger.Trace("Calculating preferred word score for '{0}'", title); + + var matchingPairs = _preferredWordService.GetMatchingPreferredWordsAndScores(series, title, 0); + + // Only add the score if there are matching terms, uncalculated + if (matchingPairs.Any()) + { + score = matchingPairs.Sum(p => p.Value); + scores.Add(score); + } + + _logger.Trace("Calculated preferred word score for '{0}': {1} ({2} match(es))", title, score, matchingPairs.Count); + } } } diff --git a/src/NzbDrone.Core/Profiles/Releases/PreferredWordService.cs b/src/NzbDrone.Core/Profiles/Releases/PreferredWordService.cs index 5b1b51bdc..5e2214de9 100644 --- a/src/NzbDrone.Core/Profiles/Releases/PreferredWordService.cs +++ b/src/NzbDrone.Core/Profiles/Releases/PreferredWordService.cs @@ -10,6 +10,7 @@ namespace NzbDrone.Core.Profiles.Releases public interface IPreferredWordService { int Calculate(Series series, string title, int indexerId); + List> GetMatchingPreferredWordsAndScores(Series series, string title, int indexerId); PreferredWordMatchResults GetMatchingPreferredWords(Series series, string title); } @@ -30,6 +31,16 @@ namespace NzbDrone.Core.Profiles.Releases { _logger.Trace("Calculating preferred word score for '{0}'", title); + var matchingPairs = GetMatchingPreferredWordsAndScores(series, title, indexerId); + var score = matchingPairs.Sum(p => p.Value); + + _logger.Trace("Calculated preferred word score for '{0}': {1} ({2} match(es))", title, score, matchingPairs.Count); + + return score; + } + + public List> GetMatchingPreferredWordsAndScores(Series series, string title, int indexerId) + { var releaseProfiles = _releaseProfileService.EnabledForTags(series.Tags, indexerId); var matchingPairs = new List>(); @@ -46,11 +57,7 @@ namespace NzbDrone.Core.Profiles.Releases } } - var score = matchingPairs.Sum(p => p.Value); - - _logger.Trace("Calculated preferred word score for '{0}': {1}", title, score); - - return score; + return matchingPairs; } public PreferredWordMatchResults GetMatchingPreferredWords(Series series, string title)