diff --git a/src/NzbDrone.Core.Test/Download/TrackedDownloads/TrackedDownloadServiceFixture.cs b/src/NzbDrone.Core.Test/Download/TrackedDownloads/TrackedDownloadServiceFixture.cs index 4072e0e9e..81ff483a0 100644 --- a/src/NzbDrone.Core.Test/Download/TrackedDownloads/TrackedDownloadServiceFixture.cs +++ b/src/NzbDrone.Core.Test/Download/TrackedDownloads/TrackedDownloadServiceFixture.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using FluentAssertions; using Moq; @@ -136,5 +136,164 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads trackedDownloads.Should().HaveCount(1); trackedDownloads.First().RemoteAlbum.Should().BeNull(); } + + [Test] + public void should_unmap_tracked_download_if_album_removed() + { + GivenDownloadHistory(); + + var remoteAlbum = new RemoteAlbum + { + Artist = new Artist() { Id = 5 }, + Albums = new List { new Album { Id = 4 } }, + ParsedAlbumInfo = new ParsedAlbumInfo() + { + AlbumTitle = "Audio Album", + ArtistName = "Audio Artist" + } + }; + + Mocker.GetMock() + .Setup(s => s.Map(It.Is(i => i.AlbumTitle == "Audio Album" && i.ArtistName == "Audio Artist"), It.IsAny(), It.IsAny>())) + .Returns(remoteAlbum); + + var client = new DownloadClientDefinition() + { + Id = 1, + Protocol = DownloadProtocol.Torrent + }; + + var item = new DownloadClientItem() + { + Title = "Audio Artist - Audio Album [2018 - FLAC]", + DownloadId = "35238", + DownloadClientInfo = new DownloadClientItemClientInfo + { + Protocol = client.Protocol, + Id = client.Id, + Name = client.Name + } + }; + + Subject.TrackDownload(client, item); + Subject.GetTrackedDownloads().Should().HaveCount(1); + + // simulate deletion - album no longer maps + Mocker.GetMock() + .Setup(s => s.Map(It.Is(i => i.AlbumTitle == "Audio Album" && i.ArtistName == "Audio Artist"), It.IsAny(), It.IsAny>())) + .Returns(default(RemoteAlbum)); + + Subject.Handle(new AlbumInfoRefreshedEvent(remoteAlbum.Artist, new List(), new List(), remoteAlbum.Albums)); + + var trackedDownloads = Subject.GetTrackedDownloads(); + trackedDownloads.Should().HaveCount(1); + trackedDownloads.First().RemoteAlbum.Should().BeNull(); + } + + [Test] + public void should_not_throw_when_processing_deleted_albums() + { + GivenDownloadHistory(); + + var remoteAlbum = new RemoteAlbum + { + Artist = new Artist() { Id = 5 }, + Albums = new List { new Album { Id = 4 } }, + ParsedAlbumInfo = new ParsedAlbumInfo() + { + AlbumTitle = "Audio Album", + ArtistName = "Audio Artist" + } + }; + + Mocker.GetMock() + .Setup(s => s.Map(It.Is(i => i.AlbumTitle == "Audio Album" && i.ArtistName == "Audio Artist"), It.IsAny(), It.IsAny>())) + .Returns(remoteAlbum); + + var client = new DownloadClientDefinition() + { + Id = 1, + Protocol = DownloadProtocol.Torrent + }; + + var item = new DownloadClientItem() + { + Title = "Audio Artist - Audio Album [2018 - FLAC]", + DownloadId = "35238", + DownloadClientInfo = new DownloadClientItemClientInfo + { + Protocol = client.Protocol, + Id = client.Id, + Name = client.Name + } + }; + + Subject.TrackDownload(client, item); + Subject.GetTrackedDownloads().Should().HaveCount(1); + + // simulate deletion - album no longer maps + Mocker.GetMock() + .Setup(s => s.Map(It.Is(i => i.AlbumTitle == "Audio Album" && i.ArtistName == "Audio Artist"), It.IsAny(), It.IsAny>())) + .Returns(default(RemoteAlbum)); + + Subject.Handle(new AlbumInfoRefreshedEvent(remoteAlbum.Artist, new List(), new List(), remoteAlbum.Albums)); + + var trackedDownloads = Subject.GetTrackedDownloads(); + trackedDownloads.Should().HaveCount(1); + trackedDownloads.First().RemoteAlbum.Should().BeNull(); + } + + [Test] + public void should_not_throw_when_processing_deleted_artist() + { + GivenDownloadHistory(); + + var remoteAlbum = new RemoteAlbum + { + Artist = new Artist() { Id = 5 }, + Albums = new List { new Album { Id = 4 } }, + ParsedAlbumInfo = new ParsedAlbumInfo() + { + AlbumTitle = "Audio Album", + ArtistName = "Audio Artist" + } + }; + + Mocker.GetMock() + .Setup(s => s.Map(It.Is(i => i.AlbumTitle == "Audio Album" && i.ArtistName == "Audio Artist"), It.IsAny(), It.IsAny>())) + .Returns(remoteAlbum); + + var client = new DownloadClientDefinition() + { + Id = 1, + Protocol = DownloadProtocol.Torrent + }; + + var item = new DownloadClientItem() + { + Title = "Audio Artist - Audio Album [2018 - FLAC]", + DownloadId = "35238", + DownloadClientInfo = new DownloadClientItemClientInfo + { + Protocol = client.Protocol, + Id = client.Id, + Name = client.Name + } + }; + + Subject.TrackDownload(client, item); + Subject.GetTrackedDownloads().Should().HaveCount(1); + + // simulate deletion - album no longer maps + Mocker.GetMock() + .Setup(s => s.Map(It.Is(i => i.AlbumTitle == "Audio Album" && i.ArtistName == "Audio Artist"), It.IsAny(), It.IsAny>())) + .Returns(default(RemoteAlbum)); + + Subject.Handle(new ArtistsDeletedEvent(new List { remoteAlbum.Artist }, true, true)); + + var trackedDownloads = Subject.GetTrackedDownloads(); + trackedDownloads.Should().HaveCount(1); + trackedDownloads.First().RemoteAlbum.Should().BeNull(); + } } } diff --git a/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownloadService.cs b/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownloadService.cs index e207a1598..4725722a0 100644 --- a/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownloadService.cs +++ b/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownloadService.cs @@ -56,22 +56,11 @@ namespace NzbDrone.Core.Download.TrackedDownloads return _cache.Find(downloadId); } - public void UpdateAlbumCache(int albumId) + private void UpdateCachedItem(TrackedDownload trackedDownload) { - var updateCacheItems = _cache.Values.Where(x => x.RemoteAlbum != null && x.RemoteAlbum.Albums.Any(a => a.Id == albumId)).ToList(); + var parsedAlbumInfo = Parser.Parser.ParseAlbumTitle(trackedDownload.DownloadItem.Title); - foreach (var item in updateCacheItems) - { - var parsedAlbumInfo = Parser.Parser.ParseAlbumTitle(item.DownloadItem.Title); - item.RemoteAlbum = null; - - if (parsedAlbumInfo != null) - { - item.RemoteAlbum = _parsingService.Map(parsedAlbumInfo); - } - } - - _eventAggregator.PublishEvent(new TrackedDownloadRefreshedEvent(GetTrackedDownloads())); + trackedDownload.RemoteAlbum = parsedAlbumInfo == null ? null : _parsingService.Map(parsedAlbumInfo, 0, new List { }); } public void StopTracking(string downloadId) @@ -259,7 +248,54 @@ namespace NzbDrone.Core.Download.TrackedDownloads public void Handle(AlbumDeletedEvent message) { - UpdateAlbumCache(message.Album.Id); + var cachedItems = _cache.Values.Where(x => x.RemoteAlbum != null && x.RemoteAlbum.Albums.Any(a => a.Id == message.Album.Id)).ToList(); + + if (cachedItems.Any()) + { + cachedItems.ForEach(UpdateCachedItem); + + _eventAggregator.PublishEvent(new TrackedDownloadRefreshedEvent(GetTrackedDownloads())); + } + } + + public void Handle(AlbumInfoRefreshedEvent message) + { + var needsToUpdate = false; + + foreach (var album in message.Removed) + { + var cachedItems = _cache.Values.Where(t => + t.RemoteAlbum?.Albums != null && + t.RemoteAlbum.Albums.Any(e => e.Id == album.Id)) + .ToList(); + + if (cachedItems.Any()) + { + needsToUpdate = true; + } + + cachedItems.ForEach(UpdateCachedItem); + } + + if (needsToUpdate) + { + _eventAggregator.PublishEvent(new TrackedDownloadRefreshedEvent(GetTrackedDownloads())); + } + } + + public void Handle(ArtistsDeletedEvent message) + { + var cachedItems = _cache.Values.Where(t => + t.RemoteAlbum?.Artist != null && + message.Artists.Select(a => a.Id).Contains(t.RemoteAlbum.Artist.Id)) + .ToList(); + + if (cachedItems.Any()) + { + cachedItems.ForEach(UpdateCachedItem); + + _eventAggregator.PublishEvent(new TrackedDownloadRefreshedEvent(GetTrackedDownloads())); + } } } } diff --git a/src/NzbDrone.Core/Music/Events/AlbumInfoRefreshedEvent.cs b/src/NzbDrone.Core/Music/Events/AlbumInfoRefreshedEvent.cs index 25ad9aca3..27962d728 100644 --- a/src/NzbDrone.Core/Music/Events/AlbumInfoRefreshedEvent.cs +++ b/src/NzbDrone.Core/Music/Events/AlbumInfoRefreshedEvent.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Collections.ObjectModel; using NzbDrone.Common.Messaging; @@ -9,12 +9,13 @@ namespace NzbDrone.Core.Music.Events public Artist Artist { get; set; } public ReadOnlyCollection Added { get; private set; } public ReadOnlyCollection Updated { get; private set; } + public ReadOnlyCollection Removed { get; private set; } - public AlbumInfoRefreshedEvent(Artist artist, IList added, IList updated) + public AlbumInfoRefreshedEvent(Artist artist, IList added, IList updated, IList removed) { Artist = artist; Added = new ReadOnlyCollection(added); - Updated = new ReadOnlyCollection(updated); + Removed = new ReadOnlyCollection(removed); } } } diff --git a/src/NzbDrone.Core/Music/Services/RefreshArtistService.cs b/src/NzbDrone.Core/Music/Services/RefreshArtistService.cs index 50882e9ac..a093b20b1 100644 --- a/src/NzbDrone.Core/Music/Services/RefreshArtistService.cs +++ b/src/NzbDrone.Core/Music/Services/RefreshArtistService.cs @@ -272,9 +272,9 @@ namespace NzbDrone.Core.Music _eventAggregator.PublishEvent(new ArtistRefreshCompleteEvent(entity)); } - protected override void PublishChildrenUpdatedEvent(Artist entity, List newChildren, List updateChildren) + protected override void PublishChildrenUpdatedEvent(Artist entity, List newChildren, List updateChildren, List removedChildren) { - _eventAggregator.PublishEvent(new AlbumInfoRefreshedEvent(entity, newChildren, updateChildren)); + _eventAggregator.PublishEvent(new AlbumInfoRefreshedEvent(entity, newChildren, updateChildren, removedChildren)); } private void Rescan(List artists, bool isNew, CommandTrigger trigger, bool infoUpdated) diff --git a/src/NzbDrone.Core/Music/Services/RefreshEntityServiceBase.cs b/src/NzbDrone.Core/Music/Services/RefreshEntityServiceBase.cs index b0ee23ef8..b72b0426c 100644 --- a/src/NzbDrone.Core/Music/Services/RefreshEntityServiceBase.cs +++ b/src/NzbDrone.Core/Music/Services/RefreshEntityServiceBase.cs @@ -109,7 +109,7 @@ namespace NzbDrone.Core.Music { } - protected virtual void PublishChildrenUpdatedEvent(TEntity entity, List newChildren, List updateChildren) + protected virtual void PublishChildrenUpdatedEvent(TEntity entity, List newChildren, List updateChildren, List removedChildren) { } @@ -287,7 +287,7 @@ namespace NzbDrone.Core.Music // now trigger updates var updated = RefreshChildren(sortedChildren, remoteChildren, forceChildRefresh, forceUpdateFileTags, lastUpdate); - PublishChildrenUpdatedEvent(entity, sortedChildren.Added, sortedChildren.Updated); + PublishChildrenUpdatedEvent(entity, sortedChildren.Added, sortedChildren.Updated, sortedChildren.Deleted); return updated; } }