2017-09-18 03:20:36 +00:00
|
|
|
using NLog;
|
2017-06-18 02:27:01 +00:00
|
|
|
using NzbDrone.Common.Extensions;
|
|
|
|
using NzbDrone.Core.Messaging.Events;
|
|
|
|
using NzbDrone.Core.Music.Events;
|
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Linq;
|
2018-01-18 02:28:47 +00:00
|
|
|
using NzbDrone.Core.MetadataSource;
|
|
|
|
using NzbDrone.Common.Instrumentation.Extensions;
|
|
|
|
using NzbDrone.Core.Exceptions;
|
|
|
|
using NzbDrone.Core.Messaging.Commands;
|
|
|
|
using NzbDrone.Core.Music.Commands;
|
2019-03-15 12:10:45 +00:00
|
|
|
using NzbDrone.Core.MediaFiles;
|
2019-07-09 20:14:05 +00:00
|
|
|
using NzbDrone.Core.History;
|
2017-06-18 02:27:01 +00:00
|
|
|
|
|
|
|
namespace NzbDrone.Core.Music
|
|
|
|
{
|
|
|
|
public interface IRefreshAlbumService
|
|
|
|
{
|
2019-07-09 20:14:05 +00:00
|
|
|
bool RefreshAlbumInfo(Album album, List<Album> remoteAlbums, bool forceUpdateFileTags);
|
|
|
|
bool RefreshAlbumInfo(List<Album> albums, List<Album> remoteAlbums, bool forceAlbumRefresh, bool forceUpdateFileTags);
|
2017-06-18 02:27:01 +00:00
|
|
|
}
|
|
|
|
|
2019-07-09 20:14:05 +00:00
|
|
|
public class RefreshAlbumService : RefreshEntityServiceBase<Album, AlbumRelease>, IRefreshAlbumService, IExecute<RefreshAlbumCommand>
|
2017-06-18 02:27:01 +00:00
|
|
|
{
|
|
|
|
private readonly IAlbumService _albumService;
|
2018-01-18 02:28:47 +00:00
|
|
|
private readonly IArtistService _artistService;
|
2019-07-09 20:14:05 +00:00
|
|
|
private readonly IAddArtistService _addArtistService;
|
2018-12-15 00:02:43 +00:00
|
|
|
private readonly IReleaseService _releaseService;
|
2018-01-18 02:28:47 +00:00
|
|
|
private readonly IProvideAlbumInfo _albumInfo;
|
2019-07-09 20:14:05 +00:00
|
|
|
private readonly IRefreshAlbumReleaseService _refreshAlbumReleaseService;
|
|
|
|
private readonly IMediaFileService _mediaFileService;
|
|
|
|
private readonly IHistoryService _historyService;
|
2017-06-18 02:27:01 +00:00
|
|
|
private readonly IEventAggregator _eventAggregator;
|
2018-02-13 01:32:18 +00:00
|
|
|
private readonly ICheckIfAlbumShouldBeRefreshed _checkIfAlbumShouldBeRefreshed;
|
2017-06-18 02:27:01 +00:00
|
|
|
private readonly Logger _logger;
|
|
|
|
|
2018-01-18 02:28:47 +00:00
|
|
|
public RefreshAlbumService(IAlbumService albumService,
|
|
|
|
IArtistService artistService,
|
2019-07-09 20:14:05 +00:00
|
|
|
IAddArtistService addArtistService,
|
2018-12-15 00:02:43 +00:00
|
|
|
IArtistMetadataRepository artistMetadataRepository,
|
|
|
|
IReleaseService releaseService,
|
2018-01-18 02:28:47 +00:00
|
|
|
IProvideAlbumInfo albumInfo,
|
2019-07-09 20:14:05 +00:00
|
|
|
IRefreshAlbumReleaseService refreshAlbumReleaseService,
|
|
|
|
IMediaFileService mediaFileService,
|
|
|
|
IHistoryService historyService,
|
2018-01-18 02:28:47 +00:00
|
|
|
IEventAggregator eventAggregator,
|
2018-02-13 01:32:18 +00:00
|
|
|
ICheckIfAlbumShouldBeRefreshed checkIfAlbumShouldBeRefreshed,
|
2018-01-18 02:28:47 +00:00
|
|
|
Logger logger)
|
2019-07-09 20:14:05 +00:00
|
|
|
: base(logger, artistMetadataRepository)
|
2017-06-18 02:27:01 +00:00
|
|
|
{
|
|
|
|
_albumService = albumService;
|
2018-01-18 02:28:47 +00:00
|
|
|
_artistService = artistService;
|
2019-07-09 20:14:05 +00:00
|
|
|
_addArtistService = addArtistService;
|
2018-12-15 00:02:43 +00:00
|
|
|
_releaseService = releaseService;
|
2018-01-18 02:28:47 +00:00
|
|
|
_albumInfo = albumInfo;
|
2019-07-09 20:14:05 +00:00
|
|
|
_refreshAlbumReleaseService = refreshAlbumReleaseService;
|
|
|
|
_mediaFileService = mediaFileService;
|
|
|
|
_historyService = historyService;
|
2017-06-18 02:27:01 +00:00
|
|
|
_eventAggregator = eventAggregator;
|
2018-02-13 01:32:18 +00:00
|
|
|
_checkIfAlbumShouldBeRefreshed = checkIfAlbumShouldBeRefreshed;
|
2017-06-18 02:27:01 +00:00
|
|
|
_logger = logger;
|
|
|
|
}
|
|
|
|
|
2019-07-09 20:14:05 +00:00
|
|
|
protected override RemoteData GetRemoteData(Album local, List<Album> remote)
|
2018-01-28 06:27:33 +00:00
|
|
|
{
|
2019-07-09 20:14:05 +00:00
|
|
|
var result = new RemoteData();
|
|
|
|
|
|
|
|
// remove not in remote list and ShouldDelete is true
|
|
|
|
if (remote != null &&
|
|
|
|
!remote.Any(x => x.ForeignAlbumId == local.ForeignAlbumId || x.OldForeignAlbumIds.Contains(local.ForeignAlbumId)) &&
|
|
|
|
ShouldDelete(local))
|
2018-01-28 06:27:33 +00:00
|
|
|
{
|
2019-07-09 20:14:05 +00:00
|
|
|
return result;
|
2018-01-28 06:27:33 +00:00
|
|
|
}
|
2019-07-09 20:14:05 +00:00
|
|
|
|
|
|
|
Tuple<string, Album, List<ArtistMetadata>> tuple = null;
|
2018-01-18 02:28:47 +00:00
|
|
|
try
|
|
|
|
{
|
2019-07-09 20:14:05 +00:00
|
|
|
tuple = _albumInfo.GetAlbumInfo(local.ForeignAlbumId);
|
2018-01-18 02:28:47 +00:00
|
|
|
}
|
|
|
|
catch (AlbumNotFoundException)
|
|
|
|
{
|
2019-07-09 20:14:05 +00:00
|
|
|
return result;
|
2018-01-18 02:28:47 +00:00
|
|
|
}
|
|
|
|
|
2019-04-16 00:52:43 +00:00
|
|
|
if (tuple.Item2.AlbumReleases.Value.Count == 0)
|
|
|
|
{
|
2019-07-09 20:14:05 +00:00
|
|
|
_logger.Debug($"{local} has no valid releases, removing.");
|
|
|
|
return result;
|
2019-04-16 00:52:43 +00:00
|
|
|
}
|
2019-07-09 20:14:05 +00:00
|
|
|
|
|
|
|
result.Entity = tuple.Item2;
|
|
|
|
result.Metadata = tuple.Item3;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void EnsureNewParent(Album local, Album remote)
|
|
|
|
{
|
|
|
|
// Make sure the appropriate artist exists (it could be that an album changes parent)
|
|
|
|
// The artistMetadata entry will be in the db but make sure a corresponding artist is too
|
|
|
|
// so that the album doesn't just disappear.
|
|
|
|
|
|
|
|
// TODO filter by metadata id before hitting database
|
|
|
|
_logger.Trace($"Ensuring parent artist exists [{remote.ArtistMetadata.Value.ForeignArtistId}]");
|
|
|
|
|
|
|
|
var newArtist = _artistService.FindById(remote.ArtistMetadata.Value.ForeignArtistId);
|
|
|
|
|
|
|
|
if (newArtist == null)
|
|
|
|
{
|
|
|
|
var oldArtist = local.Artist.Value;
|
|
|
|
var addArtist = new Artist {
|
|
|
|
Metadata = remote.ArtistMetadata.Value,
|
|
|
|
MetadataProfileId = oldArtist.MetadataProfileId,
|
|
|
|
QualityProfileId = oldArtist.QualityProfileId,
|
|
|
|
LanguageProfileId = oldArtist.LanguageProfileId,
|
|
|
|
RootFolderPath = oldArtist.RootFolderPath,
|
|
|
|
Monitored = oldArtist.Monitored,
|
|
|
|
AlbumFolder = oldArtist.AlbumFolder
|
|
|
|
};
|
|
|
|
_logger.Debug($"Adding missing parent artist {addArtist}");
|
|
|
|
_addArtistService.AddArtist(addArtist);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override bool ShouldDelete(Album local)
|
|
|
|
{
|
|
|
|
return !_mediaFileService.GetFilesByAlbum(local.Id).Any();
|
|
|
|
}
|
2019-04-16 00:52:43 +00:00
|
|
|
|
2019-07-09 20:14:05 +00:00
|
|
|
protected override void LogProgress(Album local)
|
|
|
|
{
|
|
|
|
_logger.ProgressInfo("Updating Info for {0}", local.Title);
|
|
|
|
}
|
2019-03-15 12:10:45 +00:00
|
|
|
|
2019-07-09 20:14:05 +00:00
|
|
|
protected override bool IsMerge(Album local, Album remote)
|
|
|
|
{
|
|
|
|
return local.ForeignAlbumId != remote.ForeignAlbumId;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override UpdateResult UpdateEntity(Album local, Album remote)
|
|
|
|
{
|
|
|
|
UpdateResult result;
|
|
|
|
|
|
|
|
if (local.Title != (remote.Title ?? "Unknown") ||
|
|
|
|
local.ForeignAlbumId != remote.ForeignAlbumId ||
|
|
|
|
local.ArtistMetadata.Value.ForeignArtistId != remote.ArtistMetadata.Value.ForeignArtistId)
|
2019-03-15 12:10:45 +00:00
|
|
|
{
|
2019-07-09 20:14:05 +00:00
|
|
|
result = UpdateResult.UpdateTags;
|
|
|
|
}
|
|
|
|
else if (!local.Equals(remote))
|
|
|
|
{
|
|
|
|
result = UpdateResult.Standard;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result = UpdateResult.None;
|
2019-03-15 12:10:45 +00:00
|
|
|
}
|
2019-07-09 20:14:05 +00:00
|
|
|
|
|
|
|
local.ArtistMetadataId = remote.ArtistMetadata.Value.Id;
|
|
|
|
local.ForeignAlbumId = remote.ForeignAlbumId;
|
|
|
|
local.OldForeignAlbumIds = remote.OldForeignAlbumIds;
|
|
|
|
local.LastInfoSync = DateTime.UtcNow;
|
|
|
|
local.CleanTitle = remote.CleanTitle;
|
|
|
|
local.Title = remote.Title ?? "Unknown";
|
|
|
|
local.Overview = remote.Overview.IsNullOrWhiteSpace() ? local.Overview : remote.Overview;
|
|
|
|
local.Disambiguation = remote.Disambiguation;
|
|
|
|
local.AlbumType = remote.AlbumType;
|
|
|
|
local.SecondaryTypes = remote.SecondaryTypes;
|
|
|
|
local.Genres = remote.Genres;
|
|
|
|
local.Images = remote.Images.Any() ? remote.Images : local.Images;
|
|
|
|
local.Links = remote.Links;
|
|
|
|
local.ReleaseDate = remote.ReleaseDate;
|
|
|
|
local.Ratings = remote.Ratings;
|
|
|
|
local.AlbumReleases = new List<AlbumRelease>();
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2019-03-15 12:10:45 +00:00
|
|
|
|
2019-07-09 20:14:05 +00:00
|
|
|
protected override UpdateResult MergeEntity(Album local, Album target, Album remote)
|
|
|
|
{
|
|
|
|
_logger.Warn($"Album {local} was merged with {remote} because the original was a duplicate.");
|
2019-03-15 12:10:45 +00:00
|
|
|
|
2019-07-09 20:14:05 +00:00
|
|
|
// move releases over to the new album and delete
|
|
|
|
var localReleases = _releaseService.GetReleasesByAlbum(local.Id);
|
|
|
|
var allReleases = localReleases.Concat(_releaseService.GetReleasesByAlbum(target.Id)).ToList();
|
|
|
|
_logger.Trace($"Moving {localReleases.Count} releases from {local} to {remote}");
|
2019-03-15 12:10:45 +00:00
|
|
|
|
2019-07-09 20:14:05 +00:00
|
|
|
// Update album ID and unmonitor all releases from the old album
|
|
|
|
allReleases.ForEach(x => x.AlbumId = target.Id);
|
|
|
|
MonitorSingleRelease(allReleases);
|
|
|
|
_releaseService.UpdateMany(allReleases);
|
2018-12-15 00:02:43 +00:00
|
|
|
|
2019-07-09 20:14:05 +00:00
|
|
|
// Update album ids for trackfiles
|
|
|
|
var files = _mediaFileService.GetFilesByAlbum(local.Id);
|
|
|
|
files.ForEach(x => x.AlbumId = target.Id);
|
|
|
|
_mediaFileService.Update(files);
|
2018-01-18 02:28:47 +00:00
|
|
|
|
2019-07-09 20:14:05 +00:00
|
|
|
// Update album ids for history
|
|
|
|
var items = _historyService.GetByAlbum(local.Id, null);
|
|
|
|
items.ForEach(x => x.AlbumId = target.Id);
|
|
|
|
_historyService.UpdateMany(items);
|
2018-01-18 02:28:47 +00:00
|
|
|
|
2019-07-09 20:14:05 +00:00
|
|
|
// Finally delete the old album
|
|
|
|
_albumService.DeleteMany(new List<Album> { local });
|
2019-03-22 09:33:48 +00:00
|
|
|
|
2019-07-09 20:14:05 +00:00
|
|
|
return UpdateResult.UpdateTags;
|
|
|
|
}
|
2019-03-22 09:33:48 +00:00
|
|
|
|
2019-07-09 20:14:05 +00:00
|
|
|
protected override Album GetEntityByForeignId(Album local)
|
|
|
|
{
|
|
|
|
return _albumService.FindById(local.ForeignAlbumId);
|
|
|
|
}
|
2018-12-15 00:02:43 +00:00
|
|
|
|
2019-07-09 20:14:05 +00:00
|
|
|
protected override void SaveEntity(Album local)
|
|
|
|
{
|
|
|
|
// Use UpdateMany to avoid firing the album edited event
|
|
|
|
_albumService.UpdateMany(new List<Album> { local });
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void DeleteEntity(Album local, bool deleteFiles)
|
|
|
|
{
|
|
|
|
_albumService.DeleteAlbum(local.Id, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override List<AlbumRelease> GetRemoteChildren(Album remote)
|
|
|
|
{
|
|
|
|
return remote.AlbumReleases.Value.DistinctBy(m => m.ForeignReleaseId).ToList();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override List<AlbumRelease> GetLocalChildren(Album entity, List<AlbumRelease> remoteChildren)
|
|
|
|
{
|
|
|
|
var children = _releaseService.GetReleasesForRefresh(entity.Id,
|
|
|
|
remoteChildren.Select(x => x.ForeignReleaseId)
|
|
|
|
.Concat(remoteChildren.SelectMany(x => x.OldForeignReleaseIds)));
|
|
|
|
|
|
|
|
// Make sure trackfiles point to the new album where we are grabbing a release from another album
|
|
|
|
var files = new List<TrackFile>();
|
|
|
|
foreach (var release in children.Where(x => x.AlbumId != entity.Id))
|
2019-03-22 09:33:48 +00:00
|
|
|
{
|
2019-07-09 20:14:05 +00:00
|
|
|
files.AddRange(_mediaFileService.GetFilesByRelease(release.Id));
|
2019-03-22 09:33:48 +00:00
|
|
|
}
|
2019-07-09 20:14:05 +00:00
|
|
|
files.ForEach(x => x.AlbumId = entity.Id);
|
|
|
|
_mediaFileService.Update(files);
|
|
|
|
|
|
|
|
return children;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override Tuple<AlbumRelease, List<AlbumRelease> > GetMatchingExistingChildren(List<AlbumRelease> existingChildren, AlbumRelease remote)
|
|
|
|
{
|
|
|
|
var existingChild = existingChildren.SingleOrDefault(x => x.ForeignReleaseId == remote.ForeignReleaseId);
|
|
|
|
var mergeChildren = existingChildren.Where(x => remote.OldForeignReleaseIds.Contains(x.ForeignReleaseId)).ToList();
|
|
|
|
return Tuple.Create(existingChild, mergeChildren);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void PrepareNewChild(AlbumRelease child, Album entity)
|
|
|
|
{
|
|
|
|
child.AlbumId = entity.Id;
|
|
|
|
child.Album = entity;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void PrepareExistingChild(AlbumRelease local, AlbumRelease remote, Album entity)
|
|
|
|
{
|
|
|
|
local.AlbumId = entity.Id;
|
|
|
|
local.Album = entity;
|
|
|
|
remote.Id = local.Id;
|
|
|
|
remote.Album = entity;
|
|
|
|
remote.AlbumId = entity.Id;
|
|
|
|
remote.Monitored = local.Monitored;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void AddChildren(List<AlbumRelease> children)
|
|
|
|
{
|
|
|
|
_releaseService.InsertMany(children);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void MonitorSingleRelease(List<AlbumRelease> releases)
|
|
|
|
{
|
|
|
|
var monitored = releases.Where(x => x.Monitored).ToList();
|
|
|
|
if (!monitored.Any())
|
2019-03-22 09:33:48 +00:00
|
|
|
{
|
2019-07-09 20:14:05 +00:00
|
|
|
monitored = releases;
|
2019-03-22 09:33:48 +00:00
|
|
|
}
|
|
|
|
|
2019-07-09 20:14:05 +00:00
|
|
|
var toMonitor = monitored.OrderByDescending(x => _mediaFileService.GetFilesByRelease(x.Id).Count)
|
|
|
|
.ThenByDescending(x => x.TrackCount)
|
|
|
|
.First();
|
2019-03-15 12:10:45 +00:00
|
|
|
|
2019-07-09 20:14:05 +00:00
|
|
|
releases.ForEach(x => x.Monitored = false);
|
|
|
|
toMonitor.Monitored = true;
|
|
|
|
}
|
2019-03-15 12:10:45 +00:00
|
|
|
|
2019-07-09 20:14:05 +00:00
|
|
|
protected override bool RefreshChildren(SortedChildren localChildren, List<AlbumRelease> remoteChildren, bool forceChildRefresh, bool forceUpdateFileTags)
|
|
|
|
{
|
|
|
|
var refreshList = localChildren.All;
|
|
|
|
|
|
|
|
// make sure only one of the releases ends up monitored
|
|
|
|
localChildren.Old.ForEach(x => x.Monitored = false);
|
|
|
|
MonitorSingleRelease(localChildren.Future);
|
2018-04-09 03:14:26 +00:00
|
|
|
|
2019-07-09 20:14:05 +00:00
|
|
|
refreshList.ForEach(x => _logger.Trace($"release: {x} monitored: {x.Monitored}"));
|
|
|
|
|
|
|
|
return _refreshAlbumReleaseService.RefreshEntityInfo(refreshList, remoteChildren, forceChildRefresh, forceUpdateFileTags);
|
|
|
|
}
|
2019-06-08 19:13:58 +00:00
|
|
|
|
2019-07-09 20:14:05 +00:00
|
|
|
public bool RefreshAlbumInfo(List<Album> albums, List<Album> remoteAlbums, bool forceAlbumRefresh, bool forceUpdateFileTags)
|
|
|
|
{
|
|
|
|
bool updated = false;
|
|
|
|
foreach (var album in albums)
|
|
|
|
{
|
|
|
|
if (forceAlbumRefresh || _checkIfAlbumShouldBeRefreshed.ShouldRefresh(album))
|
|
|
|
{
|
|
|
|
updated |= RefreshAlbumInfo(album, remoteAlbums, forceUpdateFileTags);
|
|
|
|
}
|
|
|
|
}
|
2019-06-08 19:13:58 +00:00
|
|
|
return updated;
|
2018-01-18 02:28:47 +00:00
|
|
|
}
|
|
|
|
|
2019-07-09 20:14:05 +00:00
|
|
|
public bool RefreshAlbumInfo(Album album, List<Album> remoteAlbums, bool forceUpdateFileTags)
|
|
|
|
{
|
|
|
|
return RefreshEntityInfo(album, remoteAlbums, true, forceUpdateFileTags);
|
|
|
|
}
|
|
|
|
|
2018-01-18 02:28:47 +00:00
|
|
|
public void Execute(RefreshAlbumCommand message)
|
|
|
|
{
|
|
|
|
if (message.AlbumId.HasValue)
|
|
|
|
{
|
|
|
|
var album = _albumService.GetAlbum(message.AlbumId.Value);
|
2018-12-15 00:02:43 +00:00
|
|
|
var artist = _artistService.GetArtistByMetadataId(album.ArtistMetadataId);
|
2019-07-09 20:14:05 +00:00
|
|
|
var updated = RefreshAlbumInfo(album, null, false);
|
2019-06-08 19:13:58 +00:00
|
|
|
if (updated)
|
|
|
|
{
|
|
|
|
_eventAggregator.PublishEvent(new ArtistUpdatedEvent(artist));
|
|
|
|
}
|
2018-01-18 02:28:47 +00:00
|
|
|
}
|
|
|
|
}
|
2017-06-18 02:27:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|