diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs index bd6c2304a..8a04196d6 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs @@ -55,7 +55,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests { var remoteMovie = new RemoteMovie(); remoteMovie.ParsedMovieInfo = new ParsedMovieInfo(); - remoteMovie.ParsedMovieInfo.MovieTitle = "A Movie"; + remoteMovie.ParsedMovieInfo.MovieTitles = new List { "A Movie" }; remoteMovie.ParsedMovieInfo.Year = 1998; remoteMovie.ParsedMovieInfo.Quality = quality; diff --git a/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs b/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs index 7f7728927..da1d9092f 100644 --- a/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs +++ b/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs @@ -54,7 +54,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests { Quality = quality, Year = 1998, - MovieTitle = "A Movie", + MovieTitles = new List { "A Movie" }, }, Movie = movie, diff --git a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemovePendingFixture.cs b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemovePendingFixture.cs index b7a626fcd..e405716ea 100644 --- a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemovePendingFixture.cs +++ b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemovePendingFixture.cs @@ -52,7 +52,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests _pending.Add(new PendingRelease { Id = id, - ParsedMovieInfo = new ParsedMovieInfo { MovieTitle = title, Year = year }, + ParsedMovieInfo = new ParsedMovieInfo { MovieTitles = new List { title }, Year = year }, MovieId = _movie.Id }); } diff --git a/src/NzbDrone.Core.Test/Download/TrackedDownloads/TrackedDownloadServiceFixture.cs b/src/NzbDrone.Core.Test/Download/TrackedDownloads/TrackedDownloadServiceFixture.cs index 95cad958e..3c1ec3fc9 100644 --- a/src/NzbDrone.Core.Test/Download/TrackedDownloads/TrackedDownloadServiceFixture.cs +++ b/src/NzbDrone.Core.Test/Download/TrackedDownloads/TrackedDownloadServiceFixture.cs @@ -49,13 +49,13 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads ParsedMovieInfo = new ParsedMovieInfo() { - MovieTitle = "A Movie", + MovieTitles = new List { "A Movie" }, Year = 1998 } }; Mocker.GetMock() - .Setup(s => s.Map(It.Is(i => i.MovieTitle == "A Movie"), It.IsAny(), null)) + .Setup(s => s.Map(It.Is(i => i.PrimaryMovieTitle == "A Movie"), It.IsAny(), null)) .Returns(new MappingResult { RemoteMovie = remoteEpisode }); ParseMovieTitle(); @@ -97,7 +97,7 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads ParsedMovieInfo = new ParsedMovieInfo() { - MovieTitle = "A Movie", + MovieTitles = { "A Movie" }, Year = 1998 } }; @@ -156,7 +156,7 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads ParsedMovieInfo = new ParsedMovieInfo() { - MovieTitle = "A Movie", + MovieTitles = { "A Movie" }, Year = 1998 } }; diff --git a/src/NzbDrone.Core.Test/MediaFiles/MovieImport/ImportDecisionMakerFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/MovieImport/ImportDecisionMakerFixture.cs index 2e5a7762d..953a19541 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/MovieImport/ImportDecisionMakerFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/MovieImport/ImportDecisionMakerFixture.cs @@ -73,7 +73,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport _fileInfo = new ParsedMovieInfo { - MovieTitle = "The Office", + MovieTitles = new List { "The Office" }, Year = 2018, Quality = _quality }; diff --git a/src/NzbDrone.Core.Test/MovieTests/MovieServiceTests/FindByTitleFixture.cs b/src/NzbDrone.Core.Test/MovieTests/MovieServiceTests/FindByTitleFixture.cs new file mode 100644 index 000000000..dd58b79cf --- /dev/null +++ b/src/NzbDrone.Core.Test/MovieTests/MovieServiceTests/FindByTitleFixture.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using System.Linq; +using FizzWare.NBuilder; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Movies; +using NzbDrone.Core.Movies.AlternativeTitles; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.MovieTests.MovieServiceTests +{ + [TestFixture] + public class FindByTitleFixture : CoreTest + { + private List _candidates; + + [SetUp] + public void Setup() + { + _candidates = Builder.CreateListOfSize(3) + .TheFirst(1) + .With(x => x.CleanTitle = "batman") + .With(x => x.Year = 2000) + .TheNext(1) + .With(x => x.CleanTitle = "batman") + .With(x => x.Year = 1999) + .TheRest() + .With(x => x.CleanTitle = "darkknight") + .With(x => x.Year = 2008) + .With(x => x.AlternativeTitles = new List + { + new AlternativeTitle + { + CleanTitle = "batman" + } + }) + .Build() + .ToList(); + } + + [Test] + public void should_find_by_title_year() + { + var movie = Subject.FindByTitle(new List { "batman" }, 2000, new List(), _candidates); + + movie.Should().NotBeNull(); + movie.Year.Should().Be(2000); + } + + [Test] + public void should_find_candidates_by_alt_titles() + { + var movie = Subject.FindByTitle(new List { "batman" }, 2008, new List(), _candidates); + movie.Should().NotBeNull(); + movie.Year.Should().Be(2008); + } + } +} diff --git a/src/NzbDrone.Core.Test/ParserTests/HashedReleaseFixture.cs b/src/NzbDrone.Core.Test/ParserTests/HashedReleaseFixture.cs index 5deeebc53..35cb2927b 100644 --- a/src/NzbDrone.Core.Test/ParserTests/HashedReleaseFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/HashedReleaseFixture.cs @@ -95,7 +95,7 @@ namespace NzbDrone.Core.Test.ParserTests public void should_properly_parse_hashed_releases(string path, string title, Quality quality, string releaseGroup) { var result = Parser.Parser.ParseMoviePath(path); - result.MovieTitle.Should().Be(title); + result.PrimaryMovieTitle.Should().Be(title); result.Quality.Quality.Should().Be(quality); result.ReleaseGroup.Should().Be(releaseGroup); } diff --git a/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs index c33009fd8..1c9168642 100644 --- a/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs @@ -63,7 +63,7 @@ namespace NzbDrone.Core.Test.ParserTests [TestCase("www.Torrenting.org - Movie.2008.720p.X264-DIMENSION", "Movie")] public void should_parse_movie_title(string postTitle, string title) { - Parser.Parser.ParseMovieTitle(postTitle).MovieTitle.Should().Be(title); + Parser.Parser.ParseMovieTitle(postTitle).PrimaryMovieTitle.Should().Be(title); } [TestCase("Movie.Aufbruch.nach.Pandora.Extended.2009.German.DTS.720p.BluRay.x264-SoW", "Movie Aufbruch nach Pandora", "Extended", 2009)] @@ -99,16 +99,85 @@ namespace NzbDrone.Core.Test.ParserTests ParsedMovieInfo movie = Parser.Parser.ParseMovieTitle(postTitle); using (new AssertionScope()) { - movie.MovieTitle.Should().Be(title); + movie.PrimaryMovieTitle.Should().Be(title); movie.Edition.Should().Be(edition); movie.Year.Should().Be(year); } } + [TestCase("L'hypothèse.du.tableau.volé.AKA.The.Hypothesis.of.the.Stolen.Painting.1978.1080p.CINET.WEB-DL.AAC2.0.x264-Cinefeel.mkv", + new string[] + { + "L'hypothèse du tableau volé AKA The Hypothesis of the Stolen Painting", + "L'hypothèse du tableau volé", + "The Hypothesis of the Stolen Painting" + })] + [TestCase("Akahige.AKA.Red.Beard.1965.CD1.CRiTERiON.DVDRip.XviD-KG.avi", + new string[] + { + "Akahige AKA Red Beard", + "Akahige", + "Red Beard" + })] + [TestCase("Akasen.chitai.AKA.Street.of.Shame.1956.1080p.BluRay.x264.FLAC.1.0.mkv", + new string[] + { + "Akasen chitai AKA Street of Shame", + "Akasen chitai", + "Street of Shame" + })] + [TestCase("Time.Under.Fire.(aka.Beneath.the.Bermuda.Triangle).1997.DVDRip.x264.CG-Grzechsin.mkv", + new string[] + { + "Time Under Fire (aka Beneath the Bermuda Triangle)", + "Time Under Fire", + "Beneath the Bermuda Triangle" + })] + [TestCase("Nochnoy.prodavet. AKA.Graveyard.Shift.2005.DVDRip.x264-HANDJOB.mkv", + new string[] + { + "Nochnoy prodavet AKA Graveyard Shift", + "Nochnoy prodavet", + "Graveyard Shift" + })] + [TestCase("AKA.2002.DVDRip.x264-HANDJOB.mkv", + new string[] + { + "AKA" + })] + [TestCase("Unbreakable.2000.BluRay.1080p.DTS.x264.dxva-EuReKA.mkv", + new string[] + { + "Unbreakable" + })] + [TestCase("Aka Ana (2008).avi", + new string[] + { + "Aka Ana" + })] + [TestCase("Return to Return to Nuke 'em High aka Volume 2 (2018) 1080p.mp4", + new string[] + { + "Return to Return to Nuke 'em High aka Volume 2", + "Return to Return to Nuke 'em High", + "Volume 2" + })] + public void should_parse_movie_alternative_titles(string postTitle, string[] parsedTitles) + { + var movieInfo = Parser.Parser.ParseMovieTitle(postTitle, true); + + movieInfo.MovieTitles.Count.Should().Be(parsedTitles.Length); + + for (var i = 0; i < movieInfo.MovieTitles.Count; i += 1) + { + movieInfo.MovieTitles[i].Should().Be(parsedTitles[i]); + } + } + [TestCase("(1995) Movie Name", "Movie Name")] public void should_parse_movie_folder_name(string postTitle, string title) { - Parser.Parser.ParseMovieTitle(postTitle, true).MovieTitle.Should().Be(title); + Parser.Parser.ParseMovieTitle(postTitle, true).PrimaryMovieTitle.Should().Be(title); } [TestCase("1776.1979.EXTENDED.720p.BluRay.X264-AMIABLE", 1979)] diff --git a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentMovieInfoFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentMovieInfoFixture.cs index 101183463..20a862d9b 100644 --- a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentMovieInfoFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentMovieInfoFixture.cs @@ -1,4 +1,5 @@ -using NUnit.Framework; +using System.Collections.Generic; +using NUnit.Framework; using NzbDrone.Core.Parser.Augmenters; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Qualities; @@ -17,7 +18,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests.AugmentersTests { MovieInfo = new ParsedMovieInfo { - MovieTitle = "A Movie", + MovieTitles = new List { "A Movie" }, Year = 1998, SimpleReleaseTitle = "A Movie Title 1998 Bluray 1080p", Quality = new QualityModel(Quality.Bluray1080p) diff --git a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetMovieFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetMovieFixture.cs index 8a48474b8..00dfeda3a 100644 --- a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetMovieFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetMovieFixture.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using Moq; using NUnit.Framework; using NzbDrone.Core.Movies; @@ -28,7 +29,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests Subject.GetMovie(title); Mocker.GetMock() - .Verify(s => s.FindByTitle(Parser.Parser.ParseMovieTitle(title, false).MovieTitle, It.IsAny(), null, null, null), Times.Once()); + .Verify(s => s.FindByTitle(Parser.Parser.ParseMovieTitle(title, false).MovieTitles, It.IsAny(), It.IsAny>(), null), Times.Once()); } /*[Test] diff --git a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs index bc7de24bf..86ef79d54 100644 --- a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs @@ -45,69 +45,69 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests _parsedMovieInfo = new ParsedMovieInfo { - MovieTitle = _movie.Title, + MovieTitles = new List { _movie.Title }, Languages = new List { Language.English }, Year = _movie.Year, }; _wrongYearInfo = new ParsedMovieInfo { - MovieTitle = _movie.Title, + MovieTitles = new List { _movie.Title }, Languages = new List { Language.English }, Year = 1900, }; _wrongTitleInfo = new ParsedMovieInfo { - MovieTitle = "Other Title", + MovieTitles = new List { "Other Title" }, Languages = new List { Language.English }, Year = 2015 }; _alternativeTitleInfo = new ParsedMovieInfo { - MovieTitle = _movie.AlternativeTitles.First().Title, + MovieTitles = new List { _movie.AlternativeTitles.First().Title }, Languages = new List { Language.English }, Year = _movie.Year, }; _translationTitleInfo = new ParsedMovieInfo { - MovieTitle = _movie.Translations.First().Title, + MovieTitles = new List { _movie.Translations.First().Title }, Languages = new List { Language.English }, Year = _movie.Year, }; _romanTitleInfo = new ParsedMovieInfo { - MovieTitle = "Fack Ju Göthe II", + MovieTitles = new List { "Fack Ju Göthe II" }, Languages = new List { Language.English }, Year = _movie.Year, }; _umlautInfo = new ParsedMovieInfo { - MovieTitle = "Fack Ju Goethe 2", + MovieTitles = new List { "Fack Ju Goethe 2" }, Languages = new List { Language.English }, Year = _movie.Year }; _umlautAltInfo = new ParsedMovieInfo { - MovieTitle = "Fack Ju Goethe 2: Same same", + MovieTitles = new List { "Fack Ju Goethe 2: Same same" }, Languages = new List { Language.English }, Year = _movie.Year }; _multiLanguageInfo = new ParsedMovieInfo { - MovieTitle = _movie.Title, + MovieTitles = { _movie.Title }, Languages = new List { Language.Original, Language.French } }; _multiLanguageWithOriginalInfo = new ParsedMovieInfo { - MovieTitle = _movie.Title, + MovieTitles = { _movie.Title }, Languages = new List { Language.Original, Language.French, Language.English } }; @@ -132,7 +132,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests Subject.Map(_parsedMovieInfo, "", null); Mocker.GetMock() - .Verify(v => v.FindByTitle(It.IsAny(), It.IsAny(), null, null, null), Times.Once()); + .Verify(v => v.FindByTitle(It.IsAny>(), It.IsAny(), It.IsAny>(), null), Times.Once()); } [Test] diff --git a/src/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs index c228a199f..e7332c930 100644 --- a/src/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs @@ -286,6 +286,7 @@ namespace NzbDrone.Core.Test.ParserTests [TestCase("Movie.Name.2008.BDREMUX.1080p.Bluray.AVC.DTS-HR.MA.5.1-LEGi0N")] [TestCase("Movie.Title.M.2008.USA.BluRay.Remux.1080p.MPEG-2.DD.5.1-TDD")] [TestCase("Movie.Title.2018.1080p.BluRay.REMUX.MPEG-2.DTS-HD.MA.5.1-EPSiLON")] + [TestCase("Movie.Title.II.2003.4K.BluRay.Remux.1080p.AVC.DTS-HD.MA.5.1-BMF")] public void should_parse_remux1080p_quality(string title) { ParseAndVerifyQuality(title, Source.BLURAY, false, Resolution.R1080p, Modifier.REMUX); diff --git a/src/NzbDrone.Core/CustomFormats/CustomFormatCalculationService.cs b/src/NzbDrone.Core/CustomFormats/CustomFormatCalculationService.cs index a29e4ba1c..5b4c6736a 100644 --- a/src/NzbDrone.Core/CustomFormats/CustomFormatCalculationService.cs +++ b/src/NzbDrone.Core/CustomFormats/CustomFormatCalculationService.cs @@ -76,7 +76,7 @@ namespace NzbDrone.Core.CustomFormats var info = new ParsedMovieInfo { - MovieTitle = movieFile.Movie.Title, + MovieTitles = new List() { movieFile.Movie.Title }, SimpleReleaseTitle = sceneName.SimplifyReleaseTitle(), Quality = movieFile.Quality, Languages = movieFile.Languages, @@ -112,7 +112,7 @@ namespace NzbDrone.Core.CustomFormats var info = new ParsedMovieInfo { - MovieTitle = movie.Title, + MovieTitles = new List() { movie.Title }, SimpleReleaseTitle = parsed?.SimpleReleaseTitle ?? blocklist.SourceTitle.SimplifyReleaseTitle(), Quality = blocklist.Quality, Languages = blocklist.Languages, @@ -140,7 +140,7 @@ namespace NzbDrone.Core.CustomFormats var info = new ParsedMovieInfo { - MovieTitle = movie.Title, + MovieTitles = new List() { movie.Title }, SimpleReleaseTitle = parsed?.SimpleReleaseTitle ?? history.SourceTitle.SimplifyReleaseTitle(), Quality = history.Quality, Languages = history.Languages, diff --git a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs index 89214ba1c..47dda3cef 100644 --- a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs +++ b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs @@ -77,12 +77,12 @@ namespace NzbDrone.Core.DecisionEngine MappingResult result = null; - if (parsedMovieInfo == null || parsedMovieInfo.MovieTitle.IsNullOrWhiteSpace()) + if (parsedMovieInfo == null || parsedMovieInfo.PrimaryMovieTitle.IsNullOrWhiteSpace()) { _logger.Debug("{0} could not be parsed :(.", report.Title); parsedMovieInfo = new ParsedMovieInfo { - MovieTitle = report.Title, + MovieTitles = new List() { report.Title }, SimpleReleaseTitle = report.Title.SimplifyReleaseTitle(), Year = 1290, Languages = new List { Language.Unknown }, diff --git a/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs b/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs index 6336412da..1cc40fe65 100644 --- a/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs +++ b/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs @@ -227,7 +227,7 @@ namespace NzbDrone.Core.Download.Pending var targetItem = FindPendingRelease(queueId); var movieReleases = _repository.AllByMovieId(targetItem.MovieId); - var releasesToRemove = movieReleases.Where(c => c.ParsedMovieInfo.MovieTitle == targetItem.ParsedMovieInfo.MovieTitle); + var releasesToRemove = movieReleases.Where(c => c.ParsedMovieInfo.PrimaryMovieTitle == targetItem.ParsedMovieInfo.PrimaryMovieTitle); _repository.DeleteMany(releasesToRemove.Select(c => c.Id)); } diff --git a/src/NzbDrone.Core/ImportLists/RSSImport/RSSImportParser.cs b/src/NzbDrone.Core/ImportLists/RSSImport/RSSImportParser.cs index e67e53ed2..396db4d36 100644 --- a/src/NzbDrone.Core/ImportLists/RSSImport/RSSImportParser.cs +++ b/src/NzbDrone.Core/ImportLists/RSSImport/RSSImportParser.cs @@ -146,7 +146,7 @@ namespace NzbDrone.Core.ImportLists.RSSImport if (result != null) { - releaseInfo.Title = result.MovieTitle; + releaseInfo.Title = result.PrimaryMovieTitle; releaseInfo.Year = result.Year; releaseInfo.ImdbId = result.ImdbId; } diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Manual/ManualImportService.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Manual/ManualImportService.cs index df17bac5e..c2909c7c7 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/Manual/ManualImportService.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/Manual/ManualImportService.cs @@ -170,7 +170,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual if (movie == null) { - _parsingService.GetMovie(relativeFile.Split('\\', '/')[0]); + movie = _parsingService.GetMovie(relativeFile.Split('\\', '/')[0]); } if (movie == null) @@ -189,7 +189,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual if (relativeParseInfo != null) { - movie = _movieService.FindByTitle(relativeParseInfo.MovieTitle, relativeParseInfo.Year); + movie = _movieService.FindByTitle(relativeParseInfo.PrimaryMovieTitle, relativeParseInfo.Year); } } diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs index a4377ba3b..7bf60603c 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs @@ -322,10 +322,10 @@ namespace NzbDrone.Core.MetadataSource.SkyHook var yearTerm = ""; - if (parserResult != null && parserResult.MovieTitle != title) + if (parserResult != null && parserResult.PrimaryMovieTitle != title) { //Parser found something interesting! - parserTitle = parserResult.MovieTitle.ToLower().Replace(".", " "); //TODO Update so not every period gets replaced (e.g. R.I.P.D.) + parserTitle = parserResult.PrimaryMovieTitle.ToLower().Replace(".", " "); //TODO Update so not every period gets replaced (e.g. R.I.P.D.) if (parserResult.Year > 1800) { yearTerm = parserResult.Year.ToString(); diff --git a/src/NzbDrone.Core/Movies/MovieService.cs b/src/NzbDrone.Core/Movies/MovieService.cs index 07d5d5fca..a2688328d 100644 --- a/src/NzbDrone.Core/Movies/MovieService.cs +++ b/src/NzbDrone.Core/Movies/MovieService.cs @@ -27,8 +27,8 @@ namespace NzbDrone.Core.Movies List FindByTmdbId(List tmdbids); Movie FindByTitle(string title); Movie FindByTitle(string title, int year); - Movie FindByTitle(string title, int? year, string arabicTitle, string romanTitle, List candidates); - List FindByTitleCandidates(string title, out string roman, out string arabic); + Movie FindByTitle(List titles, int? year, List otherTitles, List candidates); + List FindByTitleCandidates(List titles, out List otherTitles); Movie FindByTitleSlug(string slug); Movie FindByPath(string path); Dictionary AllMoviePaths(); @@ -106,68 +106,74 @@ namespace NzbDrone.Core.Movies public Movie FindByTitle(string title) { - var candidates = FindByTitleCandidates(title, out var arabicTitle, out var romanTitle); + var candidates = FindByTitleCandidates(new List { title }, out var otherTitles); - return FindByTitle(title, null, arabicTitle, romanTitle, candidates); + return FindByTitle(new List { title }, null, otherTitles, candidates); } public Movie FindByTitle(string title, int year) { - var candidates = FindByTitleCandidates(title, out var arabicTitle, out var romanTitle); + var candidates = FindByTitleCandidates(new List { title }, out var otherTitles); - return FindByTitle(title, year, arabicTitle, romanTitle, candidates); + return FindByTitle(new List { title }, year, otherTitles, candidates); } - public Movie FindByTitle(string cleanTitle, int? year, string arabicTitle, string romanTitle, List candidates) + public Movie FindByTitle(List cleanTitles, int? year, List otherTitles, List candidates) { - var result = candidates.Where(x => x.CleanTitle == cleanTitle).FirstWithYear(year); + var result = candidates.Where(x => cleanTitles.Contains(x.CleanTitle)).FirstWithYear(year); if (result == null) { result = - candidates.Where(movie => movie.CleanTitle == arabicTitle).FirstWithYear(year) ?? - candidates.Where(movie => movie.CleanTitle == romanTitle).FirstWithYear(year); + candidates.Where(movie => otherTitles.Contains(movie.CleanTitle)).FirstWithYear(year); } if (result == null) { result = candidates - .Where(m => m.AlternativeTitles.Any(t => t.CleanTitle == cleanTitle || - t.CleanTitle == arabicTitle || - t.CleanTitle == romanTitle)) + .Where(m => m.AlternativeTitles.Any(t => cleanTitles.Contains(t.CleanTitle) || + otherTitles.Contains(t.CleanTitle))) .FirstWithYear(year); } if (result == null) { result = candidates - .Where(m => m.Translations.Any(t => t.CleanTitle == cleanTitle || - t.CleanTitle == arabicTitle || - t.CleanTitle == romanTitle)) + .Where(m => m.Translations.Any(t => cleanTitles.Contains(t.CleanTitle) || + otherTitles.Contains(t.CleanTitle))) .FirstWithYear(year); } return result; } - public List FindByTitleCandidates(string title, out string arabicTitle, out string romanTitle) + public List FindByTitleCandidates(List titles, out List otherTitles) { - var cleanTitle = title.CleanMovieTitle().ToLowerInvariant(); - romanTitle = cleanTitle; - arabicTitle = cleanTitle; + var lookupTitles = new List(); + otherTitles = new List(); - foreach (var arabicRomanNumeral in RomanNumeralParser.GetArabicRomanNumeralsMapping()) + foreach (var title in titles) { - var arabicNumber = arabicRomanNumeral.ArabicNumeralAsString; - var romanNumber = arabicRomanNumeral.RomanNumeral; + var cleanTitle = title.CleanMovieTitle().ToLowerInvariant(); + var romanTitle = cleanTitle; + var arabicTitle = cleanTitle; - romanTitle = romanTitle.Replace(arabicNumber, romanNumber); - arabicTitle = arabicTitle.Replace(romanNumber, arabicNumber); + foreach (var arabicRomanNumeral in RomanNumeralParser.GetArabicRomanNumeralsMapping()) + { + var arabicNumber = arabicRomanNumeral.ArabicNumeralAsString; + var romanNumber = arabicRomanNumeral.RomanNumeral; + + romanTitle = romanTitle.Replace(arabicNumber, romanNumber); + arabicTitle = arabicTitle.Replace(romanNumber, arabicNumber); + } + + romanTitle = romanTitle.ToLowerInvariant(); + + otherTitles.AddRange(new List { arabicTitle, romanTitle }); + lookupTitles.AddRange(new List { cleanTitle, arabicTitle, romanTitle }); } - romanTitle = romanTitle.ToLowerInvariant(); - - return _movieRepository.FindByTitles(new List { cleanTitle, arabicTitle, romanTitle }); + return _movieRepository.FindByTitles(lookupTitles); } public Movie FindByImdbId(string imdbid) diff --git a/src/NzbDrone.Core/Parser/Augmenters/AugmentWithReleaseInfo.cs b/src/NzbDrone.Core/Parser/Augmenters/AugmentWithReleaseInfo.cs index 369aa46b7..6896d2be8 100644 --- a/src/NzbDrone.Core/Parser/Augmenters/AugmentWithReleaseInfo.cs +++ b/src/NzbDrone.Core/Parser/Augmenters/AugmentWithReleaseInfo.cs @@ -39,7 +39,7 @@ namespace NzbDrone.Core.Parser.Augmenters } // First, let's augment the language! var languageTitle = movieInfo.SimpleReleaseTitle; - if (movieInfo.MovieTitle.IsNotNullOrWhiteSpace()) + if (movieInfo.PrimaryMovieTitle.IsNotNullOrWhiteSpace()) { if (languageTitle.ToLower().Contains("multi") && indexerSettings?.MultiLanguages?.Any() == true) { diff --git a/src/NzbDrone.Core/Parser/Model/ParsedMovieInfo.cs b/src/NzbDrone.Core/Parser/Model/ParsedMovieInfo.cs index d24f2f4ea..8463d0a9c 100644 --- a/src/NzbDrone.Core/Parser/Model/ParsedMovieInfo.cs +++ b/src/NzbDrone.Core/Parser/Model/ParsedMovieInfo.cs @@ -7,12 +7,18 @@ namespace NzbDrone.Core.Parser.Model { public class ParsedMovieInfo { - public string MovieTitle { get; set; } + public ParsedMovieInfo() + { + MovieTitles = new List(); + Languages = new List(); + } + + public List MovieTitles { get; set; } public string OriginalTitle { get; set; } public string ReleaseTitle { get; set; } public string SimpleReleaseTitle { get; set; } public QualityModel Quality { get; set; } - public List Languages { get; set; } = new List(); + public List Languages { get; set; } public string ReleaseGroup { get; set; } public string ReleaseHash { get; set; } public string Edition { get; set; } @@ -22,9 +28,22 @@ namespace NzbDrone.Core.Parser.Model [JsonIgnore] public Dictionary ExtraInfo { get; set; } = new Dictionary(); + public string PrimaryMovieTitle + { + get + { + if (MovieTitles.Count > 0) + { + return MovieTitles[0]; + } + + return null; + } + } + public override string ToString() { - return string.Format("{0} - {1} {2}", MovieTitle, Year, Quality); + return string.Format("{0} - {1} {2}", PrimaryMovieTitle, Year, Quality); } } } diff --git a/src/NzbDrone.Core/Parser/Parser.cs b/src/NzbDrone.Core/Parser/Parser.cs index 9fa5dba9a..ed4ea6423 100644 --- a/src/NzbDrone.Core/Parser/Parser.cs +++ b/src/NzbDrone.Core/Parser/Parser.cs @@ -82,6 +82,12 @@ namespace NzbDrone.Core.Parser //Regex to detect whether the title was reversed. private static readonly Regex ReversedTitleRegex = new Regex(@"(?:^|[-._ ])(p027|p0801)[-._ ]", RegexOptions.Compiled); + //Regex to split movie titles that contain `AKA`. + private static readonly Regex AlternativeTitleRegex = new Regex(@"[ ]+AKA[ ]+", RegexOptions.IgnoreCase | RegexOptions.Compiled); + + // Regex to unbracket alternative titles. + private static readonly Regex BracketedAlternativeTitleRegex = new Regex(@"(.*) \([ ]*AKA[ ]+(.*)\)", RegexOptions.IgnoreCase | RegexOptions.Compiled); + private static readonly Regex NormalizeRegex = new Regex(@"((?:\b|_)(?(); + movieTitles.Add(movieName); + + //Delete parentheses of the form (aka ...). + var unbracketedName = BracketedAlternativeTitleRegex.Replace(movieName, "$1 AKA $2"); + + //Split by AKA and filter out empty and duplicate names. + movieTitles + .AddRange(AlternativeTitleRegex + .Split(unbracketedName) + .Where(alternativeName => alternativeName.IsNotNullOrWhiteSpace() && alternativeName != movieName)); + + result.MovieTitles = movieTitles; Logger.Debug("Movie Parsed. {0}", result); diff --git a/src/NzbDrone.Core/Parser/ParsingService.cs b/src/NzbDrone.Core/Parser/ParsingService.cs index 527b56d7f..6b97c504d 100644 --- a/src/NzbDrone.Core/Parser/ParsingService.cs +++ b/src/NzbDrone.Core/Parser/ParsingService.cs @@ -1,9 +1,7 @@ -using System; using System.Collections.Generic; using System.IO; using System.Linq; using NLog; -using NzbDrone.Core.Configuration; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Languages; @@ -29,17 +27,14 @@ namespace NzbDrone.Core.Parser private static HashSet _arabicRomanNumeralMappings; private readonly IMovieService _movieService; - private readonly IConfigService _config; private readonly IEnumerable _augmenters; private readonly Logger _logger; public ParsingService(IMovieService movieService, - IConfigService configService, IEnumerable augmenters, Logger logger) { _movieService = movieService; - _config = configService; _augmenters = augmenters; _logger = logger; @@ -186,7 +181,7 @@ namespace NzbDrone.Core.Parser } // nothing found up to here => logging that and returning null - _logger.Debug($"No matching movie {parsedMovieInfo.MovieTitle}"); + _logger.Debug($"No matching movie for titles {string.Join(", ", parsedMovieInfo.MovieTitles)} ({parsedMovieInfo.Year})"); return result; } @@ -215,12 +210,12 @@ namespace NzbDrone.Core.Parser private bool TryGetMovieByTitleAndOrYear(ParsedMovieInfo parsedMovieInfo, out MappingResult result) { - var candidates = _movieService.FindByTitleCandidates(parsedMovieInfo.MovieTitle, out var arabicTitle, out var romanTitle); + var candidates = _movieService.FindByTitleCandidates(parsedMovieInfo.MovieTitles, out var otherTitles); Movie movieByTitleAndOrYear; if (parsedMovieInfo.Year > 1800) { - movieByTitleAndOrYear = _movieService.FindByTitle(parsedMovieInfo.MovieTitle, parsedMovieInfo.Year, arabicTitle, romanTitle, candidates); + movieByTitleAndOrYear = _movieService.FindByTitle(parsedMovieInfo.MovieTitles, parsedMovieInfo.Year, otherTitles, candidates); if (movieByTitleAndOrYear != null) { result = new MappingResult { Movie = movieByTitleAndOrYear }; @@ -230,7 +225,7 @@ namespace NzbDrone.Core.Parser // Only default to not using year when one is parsed if only one movie candidate exists if (candidates != null && candidates.Count == 1) { - movieByTitleAndOrYear = _movieService.FindByTitle(parsedMovieInfo.MovieTitle, null, arabicTitle, romanTitle, candidates); + movieByTitleAndOrYear = _movieService.FindByTitle(parsedMovieInfo.MovieTitles, null, otherTitles, candidates); if (movieByTitleAndOrYear != null) { result = new MappingResult { Movie = movieByTitleAndOrYear, MappingResultType = MappingResultType.WrongYear }; @@ -242,7 +237,7 @@ namespace NzbDrone.Core.Parser return false; } - movieByTitleAndOrYear = _movieService.FindByTitle(parsedMovieInfo.MovieTitle, null, arabicTitle, romanTitle, candidates); + movieByTitleAndOrYear = _movieService.FindByTitle(parsedMovieInfo.MovieTitles, null, otherTitles, candidates); if (movieByTitleAndOrYear != null) { result = new MappingResult { Movie = movieByTitleAndOrYear }; @@ -263,7 +258,7 @@ namespace NzbDrone.Core.Parser possibleTitles.AddRange(searchCriteria.Movie.AlternativeTitles.Select(t => t.CleanTitle)); possibleTitles.AddRange(searchCriteria.Movie.Translations.Select(t => t.CleanTitle)); - var cleanTitle = parsedMovieInfo.MovieTitle.CleanMovieTitle(); + var cleanTitle = parsedMovieInfo.PrimaryMovieTitle.CleanMovieTitle(); foreach (var title in possibleTitles) { @@ -321,13 +316,13 @@ namespace NzbDrone.Core.Parser case MappingResultType.NotParsable: return $"Failed to find movie title in release name {ReleaseName}"; case MappingResultType.TitleNotFound: - return $"Could not find {RemoteMovie.ParsedMovieInfo.MovieTitle}"; + return $"Could not find {RemoteMovie.ParsedMovieInfo.PrimaryMovieTitle}"; case MappingResultType.WrongYear: return $"Failed to map movie, expected year {RemoteMovie.Movie.Year}, but found {RemoteMovie.ParsedMovieInfo.Year}"; case MappingResultType.WrongTitle: var comma = RemoteMovie.Movie.AlternativeTitles.Count > 0 ? ", " : ""; return - $"Failed to map movie, found title {RemoteMovie.ParsedMovieInfo.MovieTitle}, expected one of: {RemoteMovie.Movie.Title}{comma}{string.Join(", ", RemoteMovie.Movie.AlternativeTitles)}"; + $"Failed to map movie, found title(s) {string.Join(", ", RemoteMovie.ParsedMovieInfo.MovieTitles)}, expected one of: {RemoteMovie.Movie.Title}{comma}{string.Join(", ", RemoteMovie.Movie.AlternativeTitles)}"; default: return $"Failed to map movie for unknown reasons"; } diff --git a/src/NzbDrone.Core/Parser/SceneChecker.cs b/src/NzbDrone.Core/Parser/SceneChecker.cs index 9b6403829..3cc5c7ef0 100644 --- a/src/NzbDrone.Core/Parser/SceneChecker.cs +++ b/src/NzbDrone.Core/Parser/SceneChecker.cs @@ -1,4 +1,4 @@ -namespace NzbDrone.Core.Parser +namespace NzbDrone.Core.Parser { public static class SceneChecker { @@ -26,7 +26,7 @@ if (parsedTitle == null || parsedTitle.ReleaseGroup == null || parsedTitle.Quality.Quality == Qualities.Quality.Unknown || - string.IsNullOrWhiteSpace(parsedTitle.MovieTitle) || + string.IsNullOrWhiteSpace(parsedTitle.PrimaryMovieTitle) || string.IsNullOrWhiteSpace(parsedTitle.ReleaseTitle)) { return null; diff --git a/src/NzbDrone.Core/Qualities/Quality.cs b/src/NzbDrone.Core/Qualities/Quality.cs index 0ac874a4c..63c5b6d85 100644 --- a/src/NzbDrone.Core/Qualities/Quality.cs +++ b/src/NzbDrone.Core/Qualities/Quality.cs @@ -208,6 +208,7 @@ namespace NzbDrone.Core.Qualities public static readonly List All; public static readonly Quality[] AllLookup; + public static readonly HashSet DefaultQualityDefinitions; public static Quality FindById(int id) { diff --git a/src/NzbDrone.Integration.Test/ApiTests/ReleaseFixture.cs b/src/NzbDrone.Integration.Test/ApiTests/ReleaseFixture.cs index d0aedb2c9..0f75ef068 100644 --- a/src/NzbDrone.Integration.Test/ApiTests/ReleaseFixture.cs +++ b/src/NzbDrone.Integration.Test/ApiTests/ReleaseFixture.cs @@ -47,7 +47,7 @@ namespace NzbDrone.Integration.Test.ApiTests releaseResource.Age.Should().BeGreaterOrEqualTo(-1); releaseResource.Title.Should().NotBeNullOrWhiteSpace(); releaseResource.DownloadUrl.Should().NotBeNullOrWhiteSpace(); - releaseResource.MovieTitle.Should().NotBeNullOrWhiteSpace(); + releaseResource.MovieTitles.First().Should().NotBeNullOrWhiteSpace(); //TODO: uncomment these after moving to restsharp for rss //releaseResource.NzbInfoUrl.Should().NotBeNullOrWhiteSpace(); diff --git a/src/Radarr.Api.V3/Indexers/ReleaseResource.cs b/src/Radarr.Api.V3/Indexers/ReleaseResource.cs index 9955abf2c..e9814bbc9 100644 --- a/src/Radarr.Api.V3/Indexers/ReleaseResource.cs +++ b/src/Radarr.Api.V3/Indexers/ReleaseResource.cs @@ -30,7 +30,7 @@ namespace Radarr.Api.V3.Indexers public string ReleaseHash { get; set; } public string Title { get; set; } public bool SceneSource { get; set; } - public string MovieTitle { get; set; } + public List MovieTitles { get; set; } public List Languages { get; set; } public bool Approved { get; set; } public bool TemporarilyRejected { get; set; } @@ -86,7 +86,7 @@ namespace Radarr.Api.V3.Indexers ReleaseGroup = parsedMovieInfo.ReleaseGroup, ReleaseHash = parsedMovieInfo.ReleaseHash, Title = releaseInfo.Title, - MovieTitle = parsedMovieInfo.MovieTitle, + MovieTitles = parsedMovieInfo.MovieTitles, Languages = parsedMovieInfo.Languages, Approved = model.Approved, TemporarilyRejected = model.TemporarilyRejected,