You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Readarr/src/NzbDrone.Core/Books/Services/RefreshSeriesService.cs

173 lines
6.8 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Core.Books
{
public interface IRefreshSeriesService
{
bool RefreshSeriesInfo(int authorMetadataId, List<Series> remoteBooks, Author remoteData, bool forceBookRefresh, bool forceUpdateFileTags, DateTime? lastUpdate);
}
public class RefreshSeriesService : RefreshEntityServiceBase<Series, SeriesBookLink>, IRefreshSeriesService
{
private readonly IBookService _bookService;
private readonly ISeriesService _seriesService;
private readonly ISeriesBookLinkService _linkService;
private readonly IRefreshSeriesBookLinkService _refreshLinkService;
private readonly Logger _logger;
public RefreshSeriesService(IBookService bookService,
ISeriesService seriesService,
ISeriesBookLinkService linkService,
IRefreshSeriesBookLinkService refreshLinkService,
IAuthorMetadataService authorMetadataService,
Logger logger)
: base(logger, authorMetadataService)
{
_bookService = bookService;
_seriesService = seriesService;
_linkService = linkService;
_refreshLinkService = refreshLinkService;
_logger = logger;
}
protected override RemoteData GetRemoteData(Series local, List<Series> remote, Author data)
{
return new RemoteData
{
Entity = remote.SingleOrDefault(x => x.ForeignSeriesId == local.ForeignSeriesId)
};
}
protected override bool IsMerge(Series local, Series remote)
{
return local.ForeignSeriesId != remote.ForeignSeriesId;
}
protected override UpdateResult UpdateEntity(Series local, Series remote)
{
if (local.Equals(remote))
{
return UpdateResult.None;
}
local.UseMetadataFrom(remote);
return UpdateResult.UpdateTags;
}
protected override Series GetEntityByForeignId(Series local)
{
return _seriesService.FindById(local.ForeignSeriesId);
}
protected override void SaveEntity(Series local)
{
// Use UpdateMany to avoid firing the book edited event
_seriesService.UpdateMany(new List<Series> { local });
}
protected override void DeleteEntity(Series local, bool deleteFiles)
{
_logger.Trace($"Removing links for series {local} author {local.ForeignAuthorId}");
var children = GetLocalChildren(local, null);
_linkService.DeleteMany(children);
if (!_linkService.GetLinksBySeries(local.Id).Any())
{
_logger.Trace($"Series {local} has no links remaining, removing");
_seriesService.Delete(local.Id);
}
}
protected override List<SeriesBookLink> GetRemoteChildren(Series local, Series remote)
{
return remote.LinkItems;
}
protected override List<SeriesBookLink> GetLocalChildren(Series entity, List<SeriesBookLink> remoteChildren)
{
return _linkService.GetLinksBySeriesAndAuthor(entity.Id, entity.ForeignAuthorId);
}
protected override Tuple<SeriesBookLink, List<SeriesBookLink>> GetMatchingExistingChildren(List<SeriesBookLink> existingChildren, SeriesBookLink remote)
{
var existingChild = existingChildren.SingleOrDefault(x => x.BookId == remote.Book.Value.Id);
var mergeChildren = new List<SeriesBookLink>();
return Tuple.Create(existingChild, mergeChildren);
}
protected override void PrepareNewChild(SeriesBookLink child, Series entity)
{
child.Series = entity;
child.SeriesId = entity.Id;
child.BookId = child.Book.Value.Id;
}
protected override void PrepareExistingChild(SeriesBookLink local, SeriesBookLink remote, Series entity)
{
local.Series = entity;
local.SeriesId = entity.Id;
remote.Id = local.Id;
remote.BookId = local.BookId;
remote.SeriesId = entity.Id;
}
protected override void AddChildren(List<SeriesBookLink> children)
{
_linkService.InsertMany(children);
}
protected override bool RefreshChildren(SortedChildren localChildren, List<SeriesBookLink> remoteChildren, Author remoteData, bool forceChildRefresh, bool forceUpdateFileTags, DateTime? lastUpdate)
{
return _refreshLinkService.RefreshSeriesBookLinkInfo(localChildren.Added, localChildren.Updated, localChildren.Merged, localChildren.Deleted, localChildren.UpToDate, remoteChildren, forceUpdateFileTags);
}
public bool RefreshSeriesInfo(int authorMetadataId, List<Series> remoteSeries, Author remoteData, bool forceBookRefresh, bool forceUpdateFileTags, DateTime? lastUpdate)
{
var updated = false;
var existingByAuthor = _seriesService.GetByAuthorMetadataId(authorMetadataId);
var existingBySeries = _seriesService.FindById(remoteSeries.Select(x => x.ForeignSeriesId).ToList());
var existing = existingByAuthor.Concat(existingBySeries).GroupBy(x => x.ForeignSeriesId).Select(x => x.First()).ToList();
var books = _bookService.GetBooksByAuthorMetadataId(authorMetadataId);
var bookDict = books.ToDictionary(x => x.ForeignBookId);
var links = new List<SeriesBookLink>();
foreach (var s in remoteData.Series.Value)
{
s.LinkItems.Value.ForEach(x => x.Series = s);
links.AddRange(s.LinkItems.Value.Where(x => bookDict.ContainsKey(x.Book.Value.ForeignBookId)));
}
var grouped = links.GroupBy(x => x.Series.Value);
// Put in the links that go with the books we actually have
foreach (var group in grouped)
{
group.Key.LinkItems = group.ToList();
}
remoteSeries = grouped.Select(x => x.Key).ToList();
var toAdd = remoteSeries.ExceptBy(x => x.ForeignSeriesId, existing, x => x.ForeignSeriesId, StringComparer.Ordinal).ToList();
var all = toAdd.Union(existing).ToList();
_seriesService.InsertMany(toAdd);
foreach (var item in all)
{
item.ForeignAuthorId = remoteData.ForeignAuthorId;
updated |= RefreshEntityInfo(item, remoteSeries, remoteData, true, forceUpdateFileTags, null);
}
return updated;
}
}
}