Fixed: Parse search results using edition titles also

Fixes #1154
pull/1159/head
ta264 3 years ago
parent 9150f6889f
commit 4541d3d3b0

@ -187,7 +187,17 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_reports[0].Title = "1937 - Snow White and the Seven Dwarves";
var author = new Author { Name = "Some Author" };
var books = new List<Book> { new Book { Title = "Some Book" } };
var books = new List<Book>
{
new Book
{
Title = "Some Book",
Editions = new List<Edition>
{
new Edition { Title = "Some Edition Title" }
}
}
};
Subject.GetSearchDecision(_reports, new BookSearchCriteria { Author = author, Books = books }).ToList();

@ -29,8 +29,11 @@ namespace NzbDrone.Core.Test.ParserTests
private void GivenSearchCriteria(string authorName, string bookTitle)
{
_author.Name = authorName;
var a = new Book();
a.Title = bookTitle;
var a = new Book
{
Title = bookTitle,
Editions = new List<Edition> { new Edition { Title = bookTitle, Monitored = true } }
};
_books.Add(a);
}

@ -12,6 +12,8 @@ namespace NzbDrone.Core.Books
Edition FindByForeignEditionId(string foreignEditionId);
List<Edition> FindByBook(int id);
List<Edition> FindByAuthor(int id);
List<Edition> FindByAuthorMetadataId(int id, bool onlyMonitored);
Edition FindByTitle(int authorMetadataId, string title);
List<Edition> GetEditionsForRefresh(int bookId, IEnumerable<string> foreignEditionIds);
List<Edition> SetMonitored(Edition edition);
}
@ -63,6 +65,28 @@ namespace NzbDrone.Core.Books
.Where<Author>(a => a.Id == id));
}
public List<Edition> FindByAuthorMetadataId(int authorMetadataId, bool onlyMonitored)
{
var builder = Builder().Join<Edition, Book>((e, b) => e.BookId == b.Id)
.Where<Book>(b => b.AuthorMetadataId == authorMetadataId);
if (onlyMonitored)
{
builder = builder.Where<Edition>(e => e.Monitored == true);
}
return Query(builder);
}
public Edition FindByTitle(int authorMetadataId, string title)
{
return Query(Builder().Join<Edition, Book>((e, b) => e.BookId == b.Id)
.Where<Book>(b => b.AuthorMetadataId == authorMetadataId)
.Where<Edition>(e => e.Monitored == true)
.Where<Edition>(e => e.Title == title))
.FirstOrDefault();
}
public List<Edition> SetMonitored(Edition edition)
{
var allEditions = FindByBook(edition.BookId);

@ -1,7 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Books.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser;
namespace NzbDrone.Core.Books
{
@ -16,6 +19,9 @@ namespace NzbDrone.Core.Books
List<Edition> GetEditionsForRefresh(int bookId, IEnumerable<string> foreignEditionIds);
List<Edition> GetEditionsByBook(int bookId);
List<Edition> GetEditionsByAuthor(int authorId);
Edition FindByTitle(int authorMetadataId, string title);
Edition FindByTitleInexact(int authorMetadataId, string title);
List<Edition> GetCandidates(int authorMetadataId, string title);
List<Edition> SetMonitored(Edition edition);
}
@ -81,6 +87,40 @@ namespace NzbDrone.Core.Books
return _editionRepository.FindByAuthor(authorId);
}
public Edition FindByTitle(int authorMetadataId, string title)
{
return _editionRepository.FindByTitle(authorMetadataId, title);
}
public Edition FindByTitleInexact(int authorMetadataId, string title)
{
var books = _editionRepository.FindByAuthorMetadataId(authorMetadataId, true);
foreach (var func in EditionScoringFunctions(title))
{
var results = FindByStringInexact(books, func.Item1, func.Item2);
if (results.Count == 1)
{
return results[0];
}
}
return null;
}
public List<Edition> GetCandidates(int authorMetadataId, string title)
{
var books = _editionRepository.FindByAuthorMetadataId(authorMetadataId, true);
var output = new List<Edition>();
foreach (var func in EditionScoringFunctions(title))
{
output.AddRange(FindByStringInexact(books, func.Item1, func.Item2));
}
return output.DistinctBy(x => x.Id).ToList();
}
public List<Edition> SetMonitored(Edition edition)
{
return _editionRepository.SetMonitored(edition);
@ -91,5 +131,40 @@ namespace NzbDrone.Core.Books
var editions = GetEditionsByBook(message.Book.Id);
DeleteMany(editions);
}
private List<Tuple<Func<Edition, string, double>, string>> EditionScoringFunctions(string title)
{
Func<Func<Edition, string, double>, string, Tuple<Func<Edition, string, double>, string>> tc = Tuple.Create;
var scoringFunctions = new List<Tuple<Func<Edition, string, double>, string>>
{
tc((a, t) => a.Title.FuzzyMatch(t), title),
tc((a, t) => a.Title.FuzzyMatch(t), title.RemoveBracketsAndContents().CleanAuthorName()),
tc((a, t) => a.Title.FuzzyMatch(t), title.RemoveAfterDash().CleanAuthorName()),
tc((a, t) => a.Title.FuzzyMatch(t), title.RemoveBracketsAndContents().RemoveAfterDash().CleanAuthorName()),
tc((a, t) => t.FuzzyContains(a.Title), title)
};
return scoringFunctions;
}
private List<Edition> FindByStringInexact(List<Edition> editions, Func<Edition, string, double> scoreFunction, string title)
{
const double fuzzThreshold = 0.7;
const double fuzzGap = 0.4;
var sortedEditions = editions.Select(s => new
{
MatchProb = scoreFunction(s, title),
Edition = s
})
.ToList()
.OrderByDescending(s => s.MatchProb)
.ToList();
return sortedEditions.TakeWhile((x, i) => i == 0 || sortedEditions[i - 1].MatchProb - x.MatchProb < fuzzGap)
.TakeWhile((x, i) => x.MatchProb > fuzzThreshold || (i > 0 && sortedEditions[i - 1].MatchProb > fuzzThreshold))
.Select(x => x.Edition)
.ToList();
}
}
}

@ -351,7 +351,11 @@ namespace NzbDrone.Core.Parser
simpleTitle = CleanTorrentSuffixRegex.Replace(simpleTitle);
var bestBook = books.OrderByDescending(x => simpleTitle.FuzzyContains(x.Title)).First();
var bestBook = books
.OrderByDescending(x => simpleTitle.FuzzyContains(x.Editions.Value.Single(x => x.Monitored).Title))
.First()
.Editions.Value
.Single(x => x.Monitored);
var foundAuthor = GetTitleFuzzy(simpleTitle, authorName, out var remainder);

@ -28,15 +28,18 @@ namespace NzbDrone.Core.Parser
{
private readonly IAuthorService _authorService;
private readonly IBookService _bookService;
private readonly IEditionService _editionService;
private readonly IMediaFileService _mediaFileService;
private readonly Logger _logger;
public ParsingService(IAuthorService authorService,
IBookService bookService,
IEditionService editionService,
IMediaFileService mediaFileService,
Logger logger)
{
_bookService = bookService;
_editionService = editionService;
_authorService = authorService;
_mediaFileService = mediaFileService;
_logger = logger;
@ -127,12 +130,25 @@ namespace NzbDrone.Core.Parser
bookInfo = _bookService.FindByTitle(author.AuthorMetadataId, parsedBookInfo.BookTitle);
}
if (bookInfo == null)
{
var edition = _editionService.FindByTitle(author.AuthorMetadataId, parsedBookInfo.BookTitle);
bookInfo = edition?.Book.Value;
}
if (bookInfo == null)
{
_logger.Debug("Trying inexact book match for {0}", parsedBookInfo.BookTitle);
bookInfo = _bookService.FindByTitleInexact(author.AuthorMetadataId, parsedBookInfo.BookTitle);
}
if (bookInfo == null)
{
_logger.Debug("Trying inexact edition match for {0}", parsedBookInfo.BookTitle);
var edition = _editionService.FindByTitleInexact(author.AuthorMetadataId, parsedBookInfo.BookTitle);
bookInfo = edition?.Book.Value;
}
if (bookInfo != null)
{
result.Add(bookInfo);
@ -213,6 +229,21 @@ namespace NzbDrone.Core.Parser
bestBook = book;
}
}
var possibleEditions = _editionService.GetCandidates(author.AuthorMetadataId, title);
foreach (var edition in possibleEditions)
{
var editionMatch = title.FuzzyMatch(edition.Title, 0.5);
var score = (authorMatch.Item2 + editionMatch.Item2) / 2;
_logger.Trace($"Edition {edition} has score {score}");
if (score > bestScore)
{
bestAuthor = author;
bestBook = edition.Book.Value;
}
}
}
_logger.Trace($"Best match: {bestAuthor} {bestBook}");

Loading…
Cancel
Save