New: Support AKA release titles

Co-Authored-By: aeonnoea <46950349+0aeonnoea0@users.noreply.github.com>
This commit is contained in:
Qstick 2020-10-15 23:08:08 -04:00
parent f9dab9d780
commit 9d6614b14a
27 changed files with 258 additions and 89 deletions

View File

@ -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<string> { "A Movie" };
remoteMovie.ParsedMovieInfo.Year = 1998;
remoteMovie.ParsedMovieInfo.Quality = quality;

View File

@ -54,7 +54,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
{
Quality = quality,
Year = 1998,
MovieTitle = "A Movie",
MovieTitles = new List<string> { "A Movie" },
},
Movie = movie,

View File

@ -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<string> { title }, Year = year },
MovieId = _movie.Id
});
}

View File

@ -49,13 +49,13 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads
ParsedMovieInfo = new ParsedMovieInfo()
{
MovieTitle = "A Movie",
MovieTitles = new List<string> { "A Movie" },
Year = 1998
}
};
Mocker.GetMock<IParsingService>()
.Setup(s => s.Map(It.Is<ParsedMovieInfo>(i => i.MovieTitle == "A Movie"), It.IsAny<string>(), null))
.Setup(s => s.Map(It.Is<ParsedMovieInfo>(i => i.PrimaryMovieTitle == "A Movie"), It.IsAny<string>(), 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
}
};

View File

@ -73,7 +73,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport
_fileInfo = new ParsedMovieInfo
{
MovieTitle = "The Office",
MovieTitles = new List<string> { "The Office" },
Year = 2018,
Quality = _quality
};

View File

@ -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<MovieService>
{
private List<Movie> _candidates;
[SetUp]
public void Setup()
{
_candidates = Builder<Movie>.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<AlternativeTitle>
{
new AlternativeTitle
{
CleanTitle = "batman"
}
})
.Build()
.ToList();
}
[Test]
public void should_find_by_title_year()
{
var movie = Subject.FindByTitle(new List<string> { "batman" }, 2000, new List<string>(), _candidates);
movie.Should().NotBeNull();
movie.Year.Should().Be(2000);
}
[Test]
public void should_find_candidates_by_alt_titles()
{
var movie = Subject.FindByTitle(new List<string> { "batman" }, 2008, new List<string>(), _candidates);
movie.Should().NotBeNull();
movie.Year.Should().Be(2008);
}
}
}

View File

@ -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);
}

View File

@ -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)]

View File

@ -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<string> { "A Movie" },
Year = 1998,
SimpleReleaseTitle = "A Movie Title 1998 Bluray 1080p",
Quality = new QualityModel(Quality.Bluray1080p)

View File

@ -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<IMovieService>()
.Verify(s => s.FindByTitle(Parser.Parser.ParseMovieTitle(title, false).MovieTitle, It.IsAny<int>(), null, null, null), Times.Once());
.Verify(s => s.FindByTitle(Parser.Parser.ParseMovieTitle(title, false).MovieTitles, It.IsAny<int>(), It.IsAny<List<string>>(), null), Times.Once());
}
/*[Test]

View File

@ -45,69 +45,69 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
_parsedMovieInfo = new ParsedMovieInfo
{
MovieTitle = _movie.Title,
MovieTitles = new List<string> { _movie.Title },
Languages = new List<Language> { Language.English },
Year = _movie.Year,
};
_wrongYearInfo = new ParsedMovieInfo
{
MovieTitle = _movie.Title,
MovieTitles = new List<string> { _movie.Title },
Languages = new List<Language> { Language.English },
Year = 1900,
};
_wrongTitleInfo = new ParsedMovieInfo
{
MovieTitle = "Other Title",
MovieTitles = new List<string> { "Other Title" },
Languages = new List<Language> { Language.English },
Year = 2015
};
_alternativeTitleInfo = new ParsedMovieInfo
{
MovieTitle = _movie.AlternativeTitles.First().Title,
MovieTitles = new List<string> { _movie.AlternativeTitles.First().Title },
Languages = new List<Language> { Language.English },
Year = _movie.Year,
};
_translationTitleInfo = new ParsedMovieInfo
{
MovieTitle = _movie.Translations.First().Title,
MovieTitles = new List<string> { _movie.Translations.First().Title },
Languages = new List<Language> { Language.English },
Year = _movie.Year,
};
_romanTitleInfo = new ParsedMovieInfo
{
MovieTitle = "Fack Ju Göthe II",
MovieTitles = new List<string> { "Fack Ju Göthe II" },
Languages = new List<Language> { Language.English },
Year = _movie.Year,
};
_umlautInfo = new ParsedMovieInfo
{
MovieTitle = "Fack Ju Goethe 2",
MovieTitles = new List<string> { "Fack Ju Goethe 2" },
Languages = new List<Language> { Language.English },
Year = _movie.Year
};
_umlautAltInfo = new ParsedMovieInfo
{
MovieTitle = "Fack Ju Goethe 2: Same same",
MovieTitles = new List<string> { "Fack Ju Goethe 2: Same same" },
Languages = new List<Language> { Language.English },
Year = _movie.Year
};
_multiLanguageInfo = new ParsedMovieInfo
{
MovieTitle = _movie.Title,
MovieTitles = { _movie.Title },
Languages = new List<Language> { Language.Original, Language.French }
};
_multiLanguageWithOriginalInfo = new ParsedMovieInfo
{
MovieTitle = _movie.Title,
MovieTitles = { _movie.Title },
Languages = new List<Language> { Language.Original, Language.French, Language.English }
};
@ -132,7 +132,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
Subject.Map(_parsedMovieInfo, "", null);
Mocker.GetMock<IMovieService>()
.Verify(v => v.FindByTitle(It.IsAny<string>(), It.IsAny<int>(), null, null, null), Times.Once());
.Verify(v => v.FindByTitle(It.IsAny<List<string>>(), It.IsAny<int>(), It.IsAny<List<string>>(), null), Times.Once());
}
[Test]

View File

@ -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);

View File

@ -76,7 +76,7 @@ namespace NzbDrone.Core.CustomFormats
var info = new ParsedMovieInfo
{
MovieTitle = movieFile.Movie.Title,
MovieTitles = new List<string>() { 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<string>() { 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<string>() { movie.Title },
SimpleReleaseTitle = parsed?.SimpleReleaseTitle ?? history.SourceTitle.SimplifyReleaseTitle(),
Quality = history.Quality,
Languages = history.Languages,

View File

@ -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<string>() { report.Title },
SimpleReleaseTitle = report.Title.SimplifyReleaseTitle(),
Year = 1290,
Languages = new List<Language> { Language.Unknown },

View File

@ -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));
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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();

View File

@ -27,8 +27,8 @@ namespace NzbDrone.Core.Movies
List<Movie> FindByTmdbId(List<int> tmdbids);
Movie FindByTitle(string title);
Movie FindByTitle(string title, int year);
Movie FindByTitle(string title, int? year, string arabicTitle, string romanTitle, List<Movie> candidates);
List<Movie> FindByTitleCandidates(string title, out string roman, out string arabic);
Movie FindByTitle(List<string> titles, int? year, List<string> otherTitles, List<Movie> candidates);
List<Movie> FindByTitleCandidates(List<string> titles, out List<string> otherTitles);
Movie FindByTitleSlug(string slug);
Movie FindByPath(string path);
Dictionary<int, string> 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<string> { title }, out var otherTitles);
return FindByTitle(title, null, arabicTitle, romanTitle, candidates);
return FindByTitle(new List<string> { 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<string> { title }, out var otherTitles);
return FindByTitle(title, year, arabicTitle, romanTitle, candidates);
return FindByTitle(new List<string> { title }, year, otherTitles, candidates);
}
public Movie FindByTitle(string cleanTitle, int? year, string arabicTitle, string romanTitle, List<Movie> candidates)
public Movie FindByTitle(List<string> cleanTitles, int? year, List<string> otherTitles, List<Movie> 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<Movie> FindByTitleCandidates(string title, out string arabicTitle, out string romanTitle)
public List<Movie> FindByTitleCandidates(List<string> titles, out List<string> otherTitles)
{
var cleanTitle = title.CleanMovieTitle().ToLowerInvariant();
romanTitle = cleanTitle;
arabicTitle = cleanTitle;
var lookupTitles = new List<string>();
otherTitles = new List<string>();
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<string> { arabicTitle, romanTitle });
lookupTitles.AddRange(new List<string> { cleanTitle, arabicTitle, romanTitle });
}
romanTitle = romanTitle.ToLowerInvariant();
return _movieRepository.FindByTitles(new List<string> { cleanTitle, arabicTitle, romanTitle });
return _movieRepository.FindByTitles(lookupTitles);
}
public Movie FindByImdbId(string imdbid)

View File

@ -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)
{

View File

@ -7,12 +7,18 @@ namespace NzbDrone.Core.Parser.Model
{
public class ParsedMovieInfo
{
public string MovieTitle { get; set; }
public ParsedMovieInfo()
{
MovieTitles = new List<string>();
Languages = new List<Language>();
}
public List<string> 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<Language> Languages { get; set; } = new List<Language>();
public List<Language> 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<string, object> ExtraInfo { get; set; } = new Dictionary<string, object>();
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);
}
}
}

View File

@ -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|_)(?<!^|[^a-zA-Z0-9_']\w[^a-zA-Z0-9_'])(a(?!$|[^a-zA-Z0-9_']\w[^a-zA-Z0-9_'])|an|the|and|or|of)(?:\b|_))|\W|_",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
@ -238,7 +244,7 @@ namespace NzbDrone.Core.Parser
//TODO: Add tests for this!
var simpleReleaseTitle = SimpleReleaseTitleRegex.Replace(releaseTitle, string.Empty);
var simpleTitleReplaceString = match[0].Groups["title"].Success ? match[0].Groups["title"].Value : result.MovieTitle;
var simpleTitleReplaceString = match[0].Groups["title"].Success ? match[0].Groups["title"].Value : result.PrimaryMovieTitle;
if (simpleTitleReplaceString.IsNotNullOrWhiteSpace())
{
@ -567,7 +573,19 @@ namespace NzbDrone.Core.Parser
result.Edition = matchCollection[0].Groups["edition"].Value.Replace(".", " ");
}
result.MovieTitle = movieName;
var movieTitles = new List<string>();
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);

View File

@ -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<ArabicRomanNumeral> _arabicRomanNumeralMappings;
private readonly IMovieService _movieService;
private readonly IConfigService _config;
private readonly IEnumerable<IAugmentParsedMovieInfo> _augmenters;
private readonly Logger _logger;
public ParsingService(IMovieService movieService,
IConfigService configService,
IEnumerable<IAugmentParsedMovieInfo> 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";
}

View File

@ -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;

View File

@ -208,6 +208,7 @@ namespace NzbDrone.Core.Qualities
public static readonly List<Quality> All;
public static readonly Quality[] AllLookup;
public static readonly HashSet<QualityDefinition> DefaultQualityDefinitions;
public static Quality FindById(int id)
{

View File

@ -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();

View File

@ -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<string> MovieTitles { get; set; }
public List<Language> 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,