diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index 27934a893..fc35da267 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -13,8 +13,10 @@
+
+
diff --git a/src/NzbDrone.Core/Books/Model/Author.cs b/src/NzbDrone.Core/Books/Model/Author.cs
index 9a5364e57..cc68cc315 100644
--- a/src/NzbDrone.Core/Books/Model/Author.cs
+++ b/src/NzbDrone.Core/Books/Model/Author.cs
@@ -77,7 +77,9 @@ public override void UseDbFieldsFrom(Author other)
RootFolderPath = other.RootFolderPath;
Added = other.Added;
QualityProfileId = other.QualityProfileId;
+ QualityProfile = other.QualityProfile;
MetadataProfileId = other.MetadataProfileId;
+ MetadataProfile = other.MetadataProfile;
Tags = other.Tags;
AddOptions = other.AddOptions;
}
diff --git a/src/NzbDrone.Core/MediaFiles/BookImport/Identification/IdentificationService.cs b/src/NzbDrone.Core/MediaFiles/BookImport/Identification/IdentificationService.cs
index b99926e06..0b8e24472 100644
--- a/src/NzbDrone.Core/MediaFiles/BookImport/Identification/IdentificationService.cs
+++ b/src/NzbDrone.Core/MediaFiles/BookImport/Identification/IdentificationService.cs
@@ -74,7 +74,7 @@ public List Identify(List localTracks, IdentificationOv
// 3 find best candidate
var watch = System.Diagnostics.Stopwatch.StartNew();
- _logger.Debug("Starting track identification");
+ _logger.Debug("Starting book identification");
var releases = GetLocalBookReleases(localTracks, config.SingleRelease);
@@ -183,7 +183,7 @@ private void IdentifyRelease(LocalEdition localBookRelease, IdentificationOverri
_logger.Debug($"Best release found in {watch.ElapsedMilliseconds}ms");
- localBookRelease.PopulateMatch();
+ localBookRelease.PopulateMatch(config.KeepAllEditions);
_logger.Debug($"IdentifyRelease done in {watch.ElapsedMilliseconds}ms");
}
diff --git a/src/NzbDrone.Core/MediaFiles/BookImport/ImportDecisionMaker.cs b/src/NzbDrone.Core/MediaFiles/BookImport/ImportDecisionMaker.cs
index 3a83dc367..3bc6bfe61 100644
--- a/src/NzbDrone.Core/MediaFiles/BookImport/ImportDecisionMaker.cs
+++ b/src/NzbDrone.Core/MediaFiles/BookImport/ImportDecisionMaker.cs
@@ -42,6 +42,7 @@ public class ImportDecisionMakerConfig
public bool SingleRelease { get; set; }
public bool IncludeExisting { get; set; }
public bool AddNewAuthors { get; set; }
+ public bool KeepAllEditions { get; set; }
}
public class ImportDecisionMaker : IMakeImportDecision
diff --git a/src/NzbDrone.Core/MediaFiles/BookImport/Manual/ManualImportService.cs b/src/NzbDrone.Core/MediaFiles/BookImport/Manual/ManualImportService.cs
index 79b30023d..71a8f6173 100644
--- a/src/NzbDrone.Core/MediaFiles/BookImport/Manual/ManualImportService.cs
+++ b/src/NzbDrone.Core/MediaFiles/BookImport/Manual/ManualImportService.cs
@@ -117,7 +117,8 @@ public List GetMediaFiles(string path, string downloadId, Auth
NewDownload = true,
SingleRelease = false,
IncludeExisting = !replaceExistingFiles,
- AddNewAuthors = false
+ AddNewAuthors = false,
+ KeepAllEditions = true
};
var decision = _importDecisionMaker.GetImportDecisions(files, null, null, config);
@@ -162,7 +163,8 @@ private List ProcessFolder(string folder, string downloadId, A
NewDownload = true,
SingleRelease = false,
IncludeExisting = !replaceExistingFiles,
- AddNewAuthors = false
+ AddNewAuthors = false,
+ KeepAllEditions = true
};
var decisions = _importDecisionMaker.GetImportDecisions(authorFiles, idOverrides, itemInfo, config);
diff --git a/src/NzbDrone.Core/MetadataSource/BookInfo/BookInfoProxy.cs b/src/NzbDrone.Core/MetadataSource/BookInfo/BookInfoProxy.cs
index 9c72099a1..a0fecc42c 100644
--- a/src/NzbDrone.Core/MetadataSource/BookInfo/BookInfoProxy.cs
+++ b/src/NzbDrone.Core/MetadataSource/BookInfo/BookInfoProxy.cs
@@ -5,6 +5,9 @@
using System.Net;
using System.Text.Json;
using System.Threading;
+using LazyCache;
+using LazyCache.Providers;
+using Microsoft.Extensions.Caching.Memory;
using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Extensions;
@@ -35,7 +38,7 @@ public class BookInfoProxy : IProvideAuthorInfo, IProvideBookInfo, ISearchForNew
private readonly Logger _logger;
private readonly IMetadataRequestBuilder _requestBuilder;
private readonly ICached> _cache;
- private readonly ICached _authorCache;
+ private readonly CachingService _authorCache;
public BookInfoProxy(IHttpClient httpClient,
ICachedHttpResponseService cachedHttpClient,
@@ -55,8 +58,13 @@ public BookInfoProxy(IHttpClient httpClient,
_editionService = editionService;
_requestBuilder = requestBuilder;
_cache = cacheManager.GetCache>(GetType());
- _authorCache = cacheManager.GetRollingCache(GetType(), "authorCache", TimeSpan.FromMinutes(5));
_logger = logger;
+
+ _authorCache = new CachingService(new MemoryCacheProvider(new MemoryCache(new MemoryCacheOptions { SizeLimit = 10 })));
+ _authorCache.DefaultCachePolicy = new CacheDefaults
+ {
+ DefaultCacheDurationSeconds = 60
+ };
}
public HashSet GetChangedAuthors(DateTime startTime)
@@ -532,7 +540,16 @@ private void AddDbIds(string authorId, Book book, Dictionary PollAuthorUncached(foreignAuthorId));
+ return _authorCache.GetOrAdd(foreignAuthorId,
+ () => PollAuthorUncached(foreignAuthorId),
+ new LazyCacheEntryOptions
+ {
+ AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10),
+ ImmediateAbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10),
+ Size = 1,
+ SlidingExpiration = TimeSpan.FromMinutes(1),
+ ExpirationMode = ExpirationMode.ImmediateEviction
+ }.RegisterPostEvictionCallback((key, value, reason, state) => _logger.Debug($"Clearing cache for {key} due to {reason}")));
}
private Author PollAuthorUncached(string foreignAuthorId)
diff --git a/src/NzbDrone.Core/Parser/Model/LocalEdition.cs b/src/NzbDrone.Core/Parser/Model/LocalEdition.cs
index ca40b82ac..5d41993f1 100644
--- a/src/NzbDrone.Core/Parser/Model/LocalEdition.cs
+++ b/src/NzbDrone.Core/Parser/Model/LocalEdition.cs
@@ -4,6 +4,7 @@
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Books;
using NzbDrone.Core.MediaFiles.BookImport.Identification;
+using SixLabors.ImageSharp.Processing;
namespace NzbDrone.Core.Parser.Model
{
@@ -35,17 +36,77 @@ public LocalEdition(List tracks)
public List ExistingTracks { get; set; }
public bool NewDownload { get; set; }
- public void PopulateMatch()
+ public void PopulateMatch(bool keepAllEditions)
{
if (Edition != null)
{
LocalBooks = LocalBooks.Concat(ExistingTracks).DistinctBy(x => x.Path).ToList();
- foreach (var localTrack in LocalBooks)
+
+ if (!keepAllEditions)
{
- localTrack.Edition = Edition;
- localTrack.Book = Edition.Book.Value;
- localTrack.Author = Edition.Book.Value.Author.Value;
- localTrack.PartCount = LocalBooks.Count;
+ // Manually clone the edition / book to avoid holding references to *every* edition we have
+ // seen during the matching process
+ var edition = new Edition();
+ edition.UseMetadataFrom(Edition);
+ edition.UseDbFieldsFrom(Edition);
+
+ var fullBook = Edition.Book.Value;
+
+ var book = new Book();
+ book.UseMetadataFrom(fullBook);
+ book.UseDbFieldsFrom(fullBook);
+ book.Author.Value.UseMetadataFrom(fullBook.Author.Value);
+ book.Author.Value.UseDbFieldsFrom(fullBook.Author.Value);
+ book.Author.Value.Metadata = fullBook.AuthorMetadata.Value;
+ book.AuthorMetadata = fullBook.AuthorMetadata.Value;
+ book.BookFiles = fullBook.BookFiles;
+ book.Editions = new List { edition };
+
+ if (fullBook.SeriesLinks.IsLoaded)
+ {
+ book.SeriesLinks = fullBook.SeriesLinks.Value.Select(l => new SeriesBookLink
+ {
+ Book = book,
+ Series = new Series
+ {
+ ForeignSeriesId = l.Series.Value.ForeignSeriesId,
+ Title = l.Series.Value.Title,
+ Description = l.Series.Value.Description,
+ Numbered = l.Series.Value.Numbered,
+ WorkCount = l.Series.Value.WorkCount,
+ PrimaryWorkCount = l.Series.Value.PrimaryWorkCount
+ },
+ IsPrimary = l.IsPrimary,
+ Position = l.Position,
+ SeriesPosition = l.SeriesPosition
+ }).ToList();
+ }
+ else
+ {
+ book.SeriesLinks = fullBook.SeriesLinks;
+ }
+
+ edition.Book = book;
+
+ Edition = edition;
+
+ foreach (var localTrack in LocalBooks)
+ {
+ localTrack.Edition = edition;
+ localTrack.Book = book;
+ localTrack.Author = book.Author.Value;
+ localTrack.PartCount = LocalBooks.Count;
+ }
+ }
+ else
+ {
+ foreach (var localTrack in LocalBooks)
+ {
+ localTrack.Edition = Edition;
+ localTrack.Book = Edition.Book.Value;
+ localTrack.Author = Edition.Book.Value.Author.Value;
+ localTrack.PartCount = LocalBooks.Count;
+ }
}
}
}
diff --git a/src/NzbDrone.Core/Readarr.Core.csproj b/src/NzbDrone.Core/Readarr.Core.csproj
index e3820c66c..309b2d63d 100644
--- a/src/NzbDrone.Core/Readarr.Core.csproj
+++ b/src/NzbDrone.Core/Readarr.Core.csproj
@@ -4,9 +4,11 @@
+
+