Fixed: Errors loading queue after albums in artist are removed

Fixes #1989

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
This commit is contained in:
Qstick 2022-12-12 21:05:17 -06:00
parent 852831a6e7
commit f538feb798
5 changed files with 219 additions and 23 deletions

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
@ -136,5 +136,164 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads
trackedDownloads.Should().HaveCount(1); trackedDownloads.Should().HaveCount(1);
trackedDownloads.First().RemoteAlbum.Should().BeNull(); 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<Album> { new Album { Id = 4 } },
ParsedAlbumInfo = new ParsedAlbumInfo()
{
AlbumTitle = "Audio Album",
ArtistName = "Audio Artist"
}
};
Mocker.GetMock<IParsingService>()
.Setup(s => s.Map(It.Is<ParsedAlbumInfo>(i => i.AlbumTitle == "Audio Album" && i.ArtistName == "Audio Artist"), It.IsAny<int>(), It.IsAny<IEnumerable<int>>()))
.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<IParsingService>()
.Setup(s => s.Map(It.Is<ParsedAlbumInfo>(i => i.AlbumTitle == "Audio Album" && i.ArtistName == "Audio Artist"), It.IsAny<int>(), It.IsAny<IEnumerable<int>>()))
.Returns(default(RemoteAlbum));
Subject.Handle(new AlbumInfoRefreshedEvent(remoteAlbum.Artist, new List<Album>(), new List<Album>(), 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<Album> { new Album { Id = 4 } },
ParsedAlbumInfo = new ParsedAlbumInfo()
{
AlbumTitle = "Audio Album",
ArtistName = "Audio Artist"
}
};
Mocker.GetMock<IParsingService>()
.Setup(s => s.Map(It.Is<ParsedAlbumInfo>(i => i.AlbumTitle == "Audio Album" && i.ArtistName == "Audio Artist"), It.IsAny<int>(), It.IsAny<IEnumerable<int>>()))
.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<IParsingService>()
.Setup(s => s.Map(It.Is<ParsedAlbumInfo>(i => i.AlbumTitle == "Audio Album" && i.ArtistName == "Audio Artist"), It.IsAny<int>(), It.IsAny<IEnumerable<int>>()))
.Returns(default(RemoteAlbum));
Subject.Handle(new AlbumInfoRefreshedEvent(remoteAlbum.Artist, new List<Album>(), new List<Album>(), 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<Album> { new Album { Id = 4 } },
ParsedAlbumInfo = new ParsedAlbumInfo()
{
AlbumTitle = "Audio Album",
ArtistName = "Audio Artist"
}
};
Mocker.GetMock<IParsingService>()
.Setup(s => s.Map(It.Is<ParsedAlbumInfo>(i => i.AlbumTitle == "Audio Album" && i.ArtistName == "Audio Artist"), It.IsAny<int>(), It.IsAny<IEnumerable<int>>()))
.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<IParsingService>()
.Setup(s => s.Map(It.Is<ParsedAlbumInfo>(i => i.AlbumTitle == "Audio Album" && i.ArtistName == "Audio Artist"), It.IsAny<int>(), It.IsAny<IEnumerable<int>>()))
.Returns(default(RemoteAlbum));
Subject.Handle(new ArtistsDeletedEvent(new List<Artist> { remoteAlbum.Artist }, true, true));
var trackedDownloads = Subject.GetTrackedDownloads();
trackedDownloads.Should().HaveCount(1);
trackedDownloads.First().RemoteAlbum.Should().BeNull();
}
} }
} }

View File

@ -56,22 +56,11 @@ namespace NzbDrone.Core.Download.TrackedDownloads
return _cache.Find(downloadId); 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) trackedDownload.RemoteAlbum = parsedAlbumInfo == null ? null : _parsingService.Map(parsedAlbumInfo, 0, new List<int> { });
{
var parsedAlbumInfo = Parser.Parser.ParseAlbumTitle(item.DownloadItem.Title);
item.RemoteAlbum = null;
if (parsedAlbumInfo != null)
{
item.RemoteAlbum = _parsingService.Map(parsedAlbumInfo);
}
}
_eventAggregator.PublishEvent(new TrackedDownloadRefreshedEvent(GetTrackedDownloads()));
} }
public void StopTracking(string downloadId) public void StopTracking(string downloadId)
@ -259,7 +248,54 @@ namespace NzbDrone.Core.Download.TrackedDownloads
public void Handle(AlbumDeletedEvent message) 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()));
}
} }
} }
} }

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using NzbDrone.Common.Messaging; using NzbDrone.Common.Messaging;
@ -9,12 +9,13 @@ namespace NzbDrone.Core.Music.Events
public Artist Artist { get; set; } public Artist Artist { get; set; }
public ReadOnlyCollection<Album> Added { get; private set; } public ReadOnlyCollection<Album> Added { get; private set; }
public ReadOnlyCollection<Album> Updated { get; private set; } public ReadOnlyCollection<Album> Updated { get; private set; }
public ReadOnlyCollection<Album> Removed { get; private set; }
public AlbumInfoRefreshedEvent(Artist artist, IList<Album> added, IList<Album> updated) public AlbumInfoRefreshedEvent(Artist artist, IList<Album> added, IList<Album> updated, IList<Album> removed)
{ {
Artist = artist; Artist = artist;
Added = new ReadOnlyCollection<Album>(added); Added = new ReadOnlyCollection<Album>(added);
Updated = new ReadOnlyCollection<Album>(updated); Removed = new ReadOnlyCollection<Album>(removed);
} }
} }
} }

View File

@ -272,9 +272,9 @@ namespace NzbDrone.Core.Music
_eventAggregator.PublishEvent(new ArtistRefreshCompleteEvent(entity)); _eventAggregator.PublishEvent(new ArtistRefreshCompleteEvent(entity));
} }
protected override void PublishChildrenUpdatedEvent(Artist entity, List<Album> newChildren, List<Album> updateChildren) protected override void PublishChildrenUpdatedEvent(Artist entity, List<Album> newChildren, List<Album> updateChildren, List<Album> removedChildren)
{ {
_eventAggregator.PublishEvent(new AlbumInfoRefreshedEvent(entity, newChildren, updateChildren)); _eventAggregator.PublishEvent(new AlbumInfoRefreshedEvent(entity, newChildren, updateChildren, removedChildren));
} }
private void Rescan(List<Artist> artists, bool isNew, CommandTrigger trigger, bool infoUpdated) private void Rescan(List<Artist> artists, bool isNew, CommandTrigger trigger, bool infoUpdated)

View File

@ -109,7 +109,7 @@ namespace NzbDrone.Core.Music
{ {
} }
protected virtual void PublishChildrenUpdatedEvent(TEntity entity, List<TChild> newChildren, List<TChild> updateChildren) protected virtual void PublishChildrenUpdatedEvent(TEntity entity, List<TChild> newChildren, List<TChild> updateChildren, List<TChild> removedChildren)
{ {
} }
@ -287,7 +287,7 @@ namespace NzbDrone.Core.Music
// now trigger updates // now trigger updates
var updated = RefreshChildren(sortedChildren, remoteChildren, forceChildRefresh, forceUpdateFileTags, lastUpdate); var updated = RefreshChildren(sortedChildren, remoteChildren, forceChildRefresh, forceUpdateFileTags, lastUpdate);
PublishChildrenUpdatedEvent(entity, sortedChildren.Added, sortedChildren.Updated); PublishChildrenUpdatedEvent(entity, sortedChildren.Added, sortedChildren.Updated, sortedChildren.Deleted);
return updated; return updated;
} }
} }