New: Only refresh items that have updated on metadata server

pull/1689/head
ta264 4 years ago committed by Qstick
parent 16a23b9d79
commit 87d29ec978

@ -51,7 +51,7 @@ namespace NzbDrone.Core.Test.MusicTests
newInfo.OldForeignReleaseIds = new List<string> { _release.ForeignReleaseId }; newInfo.OldForeignReleaseIds = new List<string> { _release.ForeignReleaseId };
newInfo.Tracks = _tracks; newInfo.Tracks = _tracks;
Subject.RefreshEntityInfo(_release, new List<AlbumRelease> { newInfo }, false, false); Subject.RefreshEntityInfo(_release, new List<AlbumRelease> { newInfo }, false, false, null);
Mocker.GetMock<IReleaseService>() Mocker.GetMock<IReleaseService>()
.Verify(v => v.UpdateMany(It.Is<List<AlbumRelease>>(s => s.First().ForeignReleaseId == newInfo.ForeignReleaseId))); .Verify(v => v.UpdateMany(It.Is<List<AlbumRelease>>(s => s.First().ForeignReleaseId == newInfo.ForeignReleaseId)));
@ -120,7 +120,7 @@ namespace NzbDrone.Core.Test.MusicTests
.Setup(s => s.GetTracksForRefresh(_release.Id, It.IsAny<IEnumerable<string>>())) .Setup(s => s.GetTracksForRefresh(_release.Id, It.IsAny<IEnumerable<string>>()))
.Returns(new List<Track> { oldTrack }); .Returns(new List<Track> { oldTrack });
Subject.RefreshEntityInfo(_release, new List<AlbumRelease> { newInfo }, false, false); Subject.RefreshEntityInfo(_release, new List<AlbumRelease> { newInfo }, false, false, null);
Mocker.GetMock<IRefreshTrackService>() Mocker.GetMock<IRefreshTrackService>()
.Verify(v => v.RefreshTrackInfo(It.IsAny<List<Track>>(), .Verify(v => v.RefreshTrackInfo(It.IsAny<List<Track>>(),

@ -100,7 +100,7 @@ namespace NzbDrone.Core.Test.MusicTests
GivenNewAlbumInfo(newAlbumInfo); GivenNewAlbumInfo(newAlbumInfo);
Subject.RefreshAlbumInfo(_albums, null, false, false); Subject.RefreshAlbumInfo(_albums, null, false, false, null);
Mocker.GetMock<IAlbumService>() Mocker.GetMock<IAlbumService>()
.Verify(v => v.UpdateMany(It.Is<List<Album>>(s => s.First().ForeignAlbumId == newAlbumInfo.ForeignAlbumId))); .Verify(v => v.UpdateMany(It.Is<List<Album>>(s => s.First().ForeignAlbumId == newAlbumInfo.ForeignAlbumId)));
@ -143,7 +143,7 @@ namespace NzbDrone.Core.Test.MusicTests
GivenNewAlbumInfo(newAlbumInfo); GivenNewAlbumInfo(newAlbumInfo);
Subject.RefreshAlbumInfo(_albums, null, false, false); Subject.RefreshAlbumInfo(_albums, null, false, false, null);
// check releases moved to clashing album // check releases moved to clashing album
Mocker.GetMock<IReleaseService>() Mocker.GetMock<IReleaseService>()

@ -7,5 +7,6 @@ namespace NzbDrone.Core.MetadataSource
public interface IProvideAlbumInfo public interface IProvideAlbumInfo
{ {
Tuple<string, Album, List<ArtistMetadata>> GetAlbumInfo(string id); Tuple<string, Album, List<ArtistMetadata>> GetAlbumInfo(string id);
HashSet<string> GetChangedAlbums(DateTime startTime);
} }
} }

@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using NzbDrone.Core.Music; using NzbDrone.Core.Music;
namespace NzbDrone.Core.MetadataSource namespace NzbDrone.Core.MetadataSource
@ -5,5 +7,6 @@ namespace NzbDrone.Core.MetadataSource
public interface IProvideArtistInfo public interface IProvideArtistInfo
{ {
Artist GetArtistInfo(string lidarrId, int metadataProfileId); Artist GetArtistInfo(string lidarrId, int metadataProfileId);
HashSet<string> GetChangedArtists(DateTime startTime);
} }
} }

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
{
public class RecentUpdatesResource
{
public int Count { get; set; }
public bool Limited { get; set; }
public DateTime Since { get; set; }
public List<string> Items { get; set; }
}
}

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using NLog; using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Common.Serializer; using NzbDrone.Common.Serializer;
@ -22,6 +23,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
private readonly IAlbumService _albumService; private readonly IAlbumService _albumService;
private readonly IMetadataRequestBuilder _requestBuilder; private readonly IMetadataRequestBuilder _requestBuilder;
private readonly IMetadataProfileService _metadataProfileService; private readonly IMetadataProfileService _metadataProfileService;
private readonly ICached<HashSet<string>> _cache;
private static readonly List<string> NonAudioMedia = new List<string> { "DVD", "DVD-Video", "Blu-ray", "HD-DVD", "VCD", "SVCD", "UMD", "VHS" }; private static readonly List<string> NonAudioMedia = new List<string> { "DVD", "DVD-Video", "Blu-ray", "HD-DVD", "VCD", "SVCD", "UMD", "VHS" };
private static readonly List<string> SkippedTracks = new List<string> { "[data track]" }; private static readonly List<string> SkippedTracks = new List<string> { "[data track]" };
@ -31,16 +33,38 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
IArtistService artistService, IArtistService artistService,
IAlbumService albumService, IAlbumService albumService,
Logger logger, Logger logger,
IMetadataProfileService metadataProfileService) IMetadataProfileService metadataProfileService,
ICacheManager cacheManager)
{ {
_httpClient = httpClient; _httpClient = httpClient;
_metadataProfileService = metadataProfileService; _metadataProfileService = metadataProfileService;
_requestBuilder = requestBuilder; _requestBuilder = requestBuilder;
_artistService = artistService; _artistService = artistService;
_albumService = albumService; _albumService = albumService;
_cache = cacheManager.GetCache<HashSet<string>>(GetType());
_logger = logger; _logger = logger;
} }
public HashSet<string> GetChangedArtists(DateTime startTime)
{
var startTimeUtc = (DateTimeOffset)DateTime.SpecifyKind(startTime, DateTimeKind.Utc);
var httpRequest = _requestBuilder.GetRequestBuilder().Create()
.SetSegment("route", "recent/artist")
.AddQueryParam("since", startTimeUtc.ToUnixTimeSeconds())
.Build();
httpRequest.SuppressHttpError = true;
var httpResponse = _httpClient.Get<RecentUpdatesResource>(httpRequest);
if (httpResponse.Resource.Limited)
{
return null;
}
return new HashSet<string>(httpResponse.Resource.Items);
}
public Artist GetArtistInfo(string foreignArtistId, int metadataProfileId) public Artist GetArtistInfo(string foreignArtistId, int metadataProfileId)
{ {
_logger.Debug("Getting Artist with LidarrAPI.MetadataID of {0}", foreignArtistId); _logger.Debug("Getting Artist with LidarrAPI.MetadataID of {0}", foreignArtistId);
@ -81,6 +105,31 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
return artist; return artist;
} }
public HashSet<string> GetChangedAlbums(DateTime startTime)
{
return _cache.Get("ChangedAlbums", () => GetChangedAlbumsUncached(startTime), TimeSpan.FromMinutes(30));
}
private HashSet<string> GetChangedAlbumsUncached(DateTime startTime)
{
var startTimeUtc = (DateTimeOffset)DateTime.SpecifyKind(startTime, DateTimeKind.Utc);
var httpRequest = _requestBuilder.GetRequestBuilder().Create()
.SetSegment("route", "recent/album")
.AddQueryParam("since", startTimeUtc.ToUnixTimeSeconds())
.Build();
httpRequest.SuppressHttpError = true;
var httpResponse = _httpClient.Get<RecentUpdatesResource>(httpRequest);
if (httpResponse.Resource.Limited)
{
return null;
}
return new HashSet<string>(httpResponse.Resource.Items);
}
public IEnumerable<AlbumResource> FilterAlbums(IEnumerable<AlbumResource> albums, int metadataProfileId) public IEnumerable<AlbumResource> FilterAlbums(IEnumerable<AlbumResource> albums, int metadataProfileId)
{ {
var metadataProfile = _metadataProfileService.Exists(metadataProfileId) ? _metadataProfileService.Get(metadataProfileId) : _metadataProfileService.All().First(); var metadataProfile = _metadataProfileService.Exists(metadataProfileId) ? _metadataProfileService.Get(metadataProfileId) : _metadataProfileService.All().First();

@ -8,7 +8,7 @@ namespace NzbDrone.Core.Music
{ {
public interface IRefreshAlbumReleaseService public interface IRefreshAlbumReleaseService
{ {
bool RefreshEntityInfo(AlbumRelease entity, List<AlbumRelease> remoteEntityList, bool forceChildRefresh, bool forceUpdateFileTags); bool RefreshEntityInfo(AlbumRelease entity, List<AlbumRelease> remoteEntityList, bool forceChildRefresh, bool forceUpdateFileTags, DateTime? lastUpdate);
bool RefreshEntityInfo(List<AlbumRelease> releases, List<AlbumRelease> remoteEntityList, bool forceChildRefresh, bool forceUpdateFileTags); bool RefreshEntityInfo(List<AlbumRelease> releases, List<AlbumRelease> remoteEntityList, bool forceChildRefresh, bool forceUpdateFileTags);
} }
@ -114,7 +114,7 @@ namespace NzbDrone.Core.Music
_trackService.InsertMany(children); _trackService.InsertMany(children);
} }
protected override bool RefreshChildren(SortedChildren localChildren, List<Track> remoteChildren, bool forceChildRefresh, bool forceUpdateFileTags) protected override bool RefreshChildren(SortedChildren localChildren, List<Track> remoteChildren, bool forceChildRefresh, bool forceUpdateFileTags, DateTime? lastUpdate)
{ {
return _refreshTrackService.RefreshTrackInfo(localChildren.Added, localChildren.Updated, localChildren.Merged, localChildren.Deleted, localChildren.UpToDate, remoteChildren, forceUpdateFileTags); return _refreshTrackService.RefreshTrackInfo(localChildren.Added, localChildren.Updated, localChildren.Merged, localChildren.Deleted, localChildren.UpToDate, remoteChildren, forceUpdateFileTags);
} }

@ -19,7 +19,7 @@ namespace NzbDrone.Core.Music
public interface IRefreshAlbumService public interface IRefreshAlbumService
{ {
bool RefreshAlbumInfo(Album album, List<Album> remoteAlbums, bool forceUpdateFileTags); bool RefreshAlbumInfo(Album album, List<Album> remoteAlbums, bool forceUpdateFileTags);
bool RefreshAlbumInfo(List<Album> albums, List<Album> remoteAlbums, bool forceAlbumRefresh, bool forceUpdateFileTags); bool RefreshAlbumInfo(List<Album> albums, List<Album> remoteAlbums, bool forceAlbumRefresh, bool forceUpdateFileTags, DateTime? lastUpdate);
} }
public class RefreshAlbumService : RefreshEntityServiceBase<Album, AlbumRelease>, IRefreshAlbumService, IExecute<RefreshAlbumCommand> public class RefreshAlbumService : RefreshEntityServiceBase<Album, AlbumRelease>, IRefreshAlbumService, IExecute<RefreshAlbumCommand>
@ -295,7 +295,7 @@ namespace NzbDrone.Core.Music
toMonitor.Monitored = true; toMonitor.Monitored = true;
} }
protected override bool RefreshChildren(SortedChildren localChildren, List<AlbumRelease> remoteChildren, bool forceChildRefresh, bool forceUpdateFileTags) protected override bool RefreshChildren(SortedChildren localChildren, List<AlbumRelease> remoteChildren, bool forceChildRefresh, bool forceUpdateFileTags, DateTime? lastUpdate)
{ {
var refreshList = localChildren.All; var refreshList = localChildren.All;
@ -314,15 +314,29 @@ namespace NzbDrone.Core.Music
_eventAggregator.PublishEvent(new AlbumUpdatedEvent(_albumService.GetAlbum(entity.Id))); _eventAggregator.PublishEvent(new AlbumUpdatedEvent(_albumService.GetAlbum(entity.Id)));
} }
public bool RefreshAlbumInfo(List<Album> albums, List<Album> remoteAlbums, bool forceAlbumRefresh, bool forceUpdateFileTags) public bool RefreshAlbumInfo(List<Album> albums, List<Album> remoteAlbums, bool forceAlbumRefresh, bool forceUpdateFileTags, DateTime? lastUpdate)
{ {
bool updated = false; bool updated = false;
var updatedMusicbrainzAlbums = new HashSet<string>();
if (lastUpdate.HasValue && lastUpdate.Value.AddDays(14) > DateTime.UtcNow)
{
updatedMusicbrainzAlbums = _albumInfo.GetChangedAlbums(lastUpdate.Value);
}
foreach (var album in albums) foreach (var album in albums)
{ {
if (forceAlbumRefresh || _checkIfAlbumShouldBeRefreshed.ShouldRefresh(album)) if (forceAlbumRefresh ||
(updatedMusicbrainzAlbums == null && _checkIfAlbumShouldBeRefreshed.ShouldRefresh(album)) ||
(updatedMusicbrainzAlbums != null && updatedMusicbrainzAlbums.Contains(album.ForeignAlbumId)))
{ {
updated |= RefreshAlbumInfo(album, remoteAlbums, forceUpdateFileTags); updated |= RefreshAlbumInfo(album, remoteAlbums, forceUpdateFileTags);
} }
else
{
_logger.Debug("Skipping refresh of album: {0}", album.Title);
}
} }
return updated; return updated;
@ -330,7 +344,7 @@ namespace NzbDrone.Core.Music
public bool RefreshAlbumInfo(Album album, List<Album> remoteAlbums, bool forceUpdateFileTags) public bool RefreshAlbumInfo(Album album, List<Album> remoteAlbums, bool forceUpdateFileTags)
{ {
return RefreshEntityInfo(album, remoteAlbums, true, forceUpdateFileTags); return RefreshEntityInfo(album, remoteAlbums, true, forceUpdateFileTags, null);
} }
public void Execute(RefreshAlbumCommand message) public void Execute(RefreshAlbumCommand message)

@ -243,11 +243,11 @@ namespace NzbDrone.Core.Music
_albumService.InsertMany(children); _albumService.InsertMany(children);
} }
protected override bool RefreshChildren(SortedChildren localChildren, List<Album> remoteChildren, bool forceChildRefresh, bool forceUpdateFileTags) protected override bool RefreshChildren(SortedChildren localChildren, List<Album> remoteChildren, bool forceChildRefresh, bool forceUpdateFileTags, DateTime? lastUpdate)
{ {
// we always want to end up refreshing the albums since we don't yet have proper data // we always want to end up refreshing the albums since we don't yet have proper data
Ensure.That(localChildren.UpToDate.Count, () => localChildren.UpToDate.Count).IsLessThanOrEqualTo(0); Ensure.That(localChildren.UpToDate.Count, () => localChildren.UpToDate.Count).IsLessThanOrEqualTo(0);
return _refreshAlbumService.RefreshAlbumInfo(localChildren.All, remoteChildren, forceChildRefresh, forceUpdateFileTags); return _refreshAlbumService.RefreshAlbumInfo(localChildren.All, remoteChildren, forceChildRefresh, forceUpdateFileTags, lastUpdate);
} }
protected override void PublishEntityUpdatedEvent(Artist entity) protected override void PublishEntityUpdatedEvent(Artist entity)
@ -317,7 +317,7 @@ namespace NzbDrone.Core.Music
{ {
try try
{ {
updated |= RefreshEntityInfo(artist, null, true, false); updated |= RefreshEntityInfo(artist, null, true, false, null);
} }
catch (Exception e) catch (Exception e)
{ {
@ -348,15 +348,24 @@ namespace NzbDrone.Core.Music
var artists = _artistService.GetAllArtists().OrderBy(c => c.Name).ToList(); var artists = _artistService.GetAllArtists().OrderBy(c => c.Name).ToList();
var artistIds = artists.Select(x => x.Id).ToList(); var artistIds = artists.Select(x => x.Id).ToList();
var updatedMusicbrainzArtists = new HashSet<string>();
if (message.LastExecutionTime.HasValue && message.LastExecutionTime.Value.AddDays(14) > DateTime.UtcNow)
{
updatedMusicbrainzArtists = _artistInfo.GetChangedArtists(message.LastStartTime.Value);
}
foreach (var artist in artists) foreach (var artist in artists)
{ {
var manualTrigger = message.Trigger == CommandTrigger.Manual; var manualTrigger = message.Trigger == CommandTrigger.Manual;
if (manualTrigger || _checkIfArtistShouldBeRefreshed.ShouldRefresh(artist)) if ((updatedMusicbrainzArtists == null && _checkIfArtistShouldBeRefreshed.ShouldRefresh(artist)) ||
(updatedMusicbrainzArtists != null && updatedMusicbrainzArtists.Contains(artist.ForeignArtistId)) ||
manualTrigger)
{ {
try try
{ {
updated |= RefreshEntityInfo(artist, null, manualTrigger, false); updated |= RefreshEntityInfo(artist, null, manualTrigger, false, message.LastStartTime);
} }
catch (Exception e) catch (Exception e)
{ {

@ -94,7 +94,7 @@ namespace NzbDrone.Core.Music
protected abstract void PrepareNewChild(TChild child, TEntity entity); protected abstract void PrepareNewChild(TChild child, TEntity entity);
protected abstract void PrepareExistingChild(TChild local, TChild remote, TEntity entity); protected abstract void PrepareExistingChild(TChild local, TChild remote, TEntity entity);
protected abstract void AddChildren(List<TChild> children); protected abstract void AddChildren(List<TChild> children);
protected abstract bool RefreshChildren(SortedChildren localChildren, List<TChild> remoteChildren, bool forceChildRefresh, bool forceUpdateFileTags); protected abstract bool RefreshChildren(SortedChildren localChildren, List<TChild> remoteChildren, bool forceChildRefresh, bool forceUpdateFileTags, DateTime? lastUpdate);
protected virtual void PublishEntityUpdatedEvent(TEntity entity) protected virtual void PublishEntityUpdatedEvent(TEntity entity)
{ {
@ -108,7 +108,7 @@ namespace NzbDrone.Core.Music
{ {
} }
public bool RefreshEntityInfo(TEntity local, List<TEntity> remoteList, bool forceChildRefresh, bool forceUpdateFileTags) public bool RefreshEntityInfo(TEntity local, List<TEntity> remoteList, bool forceChildRefresh, bool forceUpdateFileTags, DateTime? lastUpdate)
{ {
bool updated = false; bool updated = false;
@ -177,7 +177,7 @@ namespace NzbDrone.Core.Music
_logger.Trace($"updated: {updated} forceUpdateFileTags: {forceUpdateFileTags}"); _logger.Trace($"updated: {updated} forceUpdateFileTags: {forceUpdateFileTags}");
var remoteChildren = GetRemoteChildren(remote); var remoteChildren = GetRemoteChildren(remote);
updated |= SortChildren(local, remoteChildren, forceChildRefresh, forceUpdateFileTags); updated |= SortChildren(local, remoteChildren, forceChildRefresh, forceUpdateFileTags, lastUpdate);
// Do this last so entity only marked as refreshed if refresh of children completed successfully // Do this last so entity only marked as refreshed if refresh of children completed successfully
_logger.Trace($"Saving {typeof(TEntity).Name} {local}"); _logger.Trace($"Saving {typeof(TEntity).Name} {local}");
@ -200,7 +200,7 @@ namespace NzbDrone.Core.Music
bool updated = false; bool updated = false;
foreach (var entity in localList) foreach (var entity in localList)
{ {
updated |= RefreshEntityInfo(entity, remoteList, forceChildRefresh, forceUpdateFileTags); updated |= RefreshEntityInfo(entity, remoteList, forceChildRefresh, forceUpdateFileTags, null);
} }
return updated; return updated;
@ -213,7 +213,7 @@ namespace NzbDrone.Core.Music
return updated ? UpdateResult.UpdateTags : UpdateResult.None; return updated ? UpdateResult.UpdateTags : UpdateResult.None;
} }
protected bool SortChildren(TEntity entity, List<TChild> remoteChildren, bool forceChildRefresh, bool forceUpdateFileTags) protected bool SortChildren(TEntity entity, List<TChild> remoteChildren, bool forceChildRefresh, bool forceUpdateFileTags, DateTime? lastUpdate)
{ {
// Get existing children (and children to be) from the database // Get existing children (and children to be) from the database
var localChildren = GetLocalChildren(entity, remoteChildren); var localChildren = GetLocalChildren(entity, remoteChildren);
@ -278,7 +278,7 @@ namespace NzbDrone.Core.Music
AddChildren(sortedChildren.Added); AddChildren(sortedChildren.Added);
// now trigger updates // now trigger updates
var updated = RefreshChildren(sortedChildren, remoteChildren, forceChildRefresh, forceUpdateFileTags); var updated = RefreshChildren(sortedChildren, remoteChildren, forceChildRefresh, forceUpdateFileTags, lastUpdate);
PublishChildrenUpdatedEvent(entity, sortedChildren.Added, sortedChildren.Updated); PublishChildrenUpdatedEvent(entity, sortedChildren.Added, sortedChildren.Updated);
return updated; return updated;

Loading…
Cancel
Save