mirror of
https://github.com/lidarr/lidarr.git
synced 2025-08-19 21:13:28 -07:00
New: Speed up Bulk Artist Deletions
This commit is contained in:
parent
8e92c55a35
commit
b9f91db3ae
18 changed files with 120 additions and 85 deletions
|
@ -95,10 +95,7 @@ namespace Lidarr.Api.V1.Artist
|
|||
{
|
||||
var resource = Request.Body.FromJson<ArtistEditorResource>();
|
||||
|
||||
foreach (var artistId in resource.ArtistIds)
|
||||
{
|
||||
_artistService.DeleteArtist(artistId, resource.DeleteFiles);
|
||||
}
|
||||
_artistService.DeleteArtists(resource.ArtistIds, resource.DeleteFiles);
|
||||
|
||||
return new object();
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace Lidarr.Api.V1.Artist
|
|||
IHandle<TrackFileDeletedEvent>,
|
||||
IHandle<ArtistUpdatedEvent>,
|
||||
IHandle<ArtistEditedEvent>,
|
||||
IHandle<ArtistDeletedEvent>,
|
||||
IHandle<ArtistsDeletedEvent>,
|
||||
IHandle<ArtistRenamedEvent>,
|
||||
IHandle<MediaCoversUpdatedEvent>
|
||||
{
|
||||
|
@ -285,9 +285,12 @@ namespace Lidarr.Api.V1.Artist
|
|||
BroadcastResourceChange(ModelAction.Updated, GetArtistResource(message.Artist));
|
||||
}
|
||||
|
||||
public void Handle(ArtistDeletedEvent message)
|
||||
public void Handle(ArtistsDeletedEvent message)
|
||||
{
|
||||
BroadcastResourceChange(ModelAction.Deleted, message.Artist.ToResource());
|
||||
foreach (var artist in message.Artists)
|
||||
{
|
||||
BroadcastResourceChange(ModelAction.Deleted, artist.ToResource());
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(ArtistRenamedEvent message)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
|
@ -16,7 +16,7 @@ namespace NzbDrone.Core.ArtistStats
|
|||
|
||||
public class ArtistStatisticsService : IArtistStatisticsService,
|
||||
IHandle<ArtistUpdatedEvent>,
|
||||
IHandle<ArtistDeletedEvent>,
|
||||
IHandle<ArtistsDeletedEvent>,
|
||||
IHandle<AlbumAddedEvent>,
|
||||
IHandle<AlbumDeletedEvent>,
|
||||
IHandle<AlbumImportedEvent>,
|
||||
|
@ -76,10 +76,14 @@ namespace NzbDrone.Core.ArtistStats
|
|||
}
|
||||
|
||||
[EventHandleOrder(EventHandleOrder.First)]
|
||||
public void Handle(ArtistDeletedEvent message)
|
||||
public void Handle(ArtistsDeletedEvent message)
|
||||
{
|
||||
_cache.Remove("AllArtists");
|
||||
_cache.Remove(message.Artist.Id.ToString());
|
||||
|
||||
foreach (var artist in message.Artists)
|
||||
{
|
||||
_cache.Remove(artist.Id.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[EventHandleOrder(EventHandleOrder.First)]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Music;
|
||||
|
@ -9,7 +9,8 @@ namespace NzbDrone.Core.Blacklisting
|
|||
{
|
||||
List<Blacklist> BlacklistedByTitle(int artistId, string sourceTitle);
|
||||
List<Blacklist> BlacklistedByTorrentInfoHash(int artistId, string torrentInfoHash);
|
||||
List<Blacklist> BlacklistedByArtist(int artistId);
|
||||
List<Blacklist> BlacklistedByArtists(List<int> artistIds);
|
||||
void DeleteForArtists(List<int> artistIds);
|
||||
}
|
||||
|
||||
public class BlacklistRepository : BasicRepository<Blacklist>, IBlacklistRepository
|
||||
|
@ -29,9 +30,14 @@ namespace NzbDrone.Core.Blacklisting
|
|||
return Query(e => e.ArtistId == artistId && e.TorrentInfoHash.Contains(torrentInfoHash));
|
||||
}
|
||||
|
||||
public List<Blacklist> BlacklistedByArtist(int artistId)
|
||||
public List<Blacklist> BlacklistedByArtists(List<int> artistIds)
|
||||
{
|
||||
return Query(b => b.ArtistId == artistId);
|
||||
return Query(x => artistIds.Contains(x.ArtistId));
|
||||
}
|
||||
|
||||
public void DeleteForArtists(List<int> artistIds)
|
||||
{
|
||||
Delete(x => artistIds.Contains(x.ArtistId));
|
||||
}
|
||||
|
||||
protected override SqlBuilder PagedBuilder() => new SqlBuilder().Join<Blacklist, Artist>((b, m) => b.ArtistId == m.Id);
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace NzbDrone.Core.Blacklisting
|
|||
|
||||
IExecute<ClearBlacklistCommand>,
|
||||
IHandle<DownloadFailedEvent>,
|
||||
IHandleAsync<ArtistDeletedEvent>
|
||||
IHandleAsync<ArtistsDeletedEvent>
|
||||
{
|
||||
private readonly IBlacklistRepository _blacklistRepository;
|
||||
|
||||
|
@ -161,11 +161,9 @@ namespace NzbDrone.Core.Blacklisting
|
|||
_blacklistRepository.Insert(blacklist);
|
||||
}
|
||||
|
||||
public void HandleAsync(ArtistDeletedEvent message)
|
||||
public void HandleAsync(ArtistsDeletedEvent message)
|
||||
{
|
||||
var blacklisted = _blacklistRepository.BlacklistedByArtist(message.Artist.Id);
|
||||
|
||||
_blacklistRepository.DeleteMany(blacklisted);
|
||||
_blacklistRepository.DeleteForArtists(message.Artists.Select(x => x.Id).ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace NzbDrone.Core.Download.Pending
|
|||
{
|
||||
public interface IPendingReleaseRepository : IBasicRepository<PendingRelease>
|
||||
{
|
||||
void DeleteByArtistId(int artistId);
|
||||
void DeleteByArtistIds(List<int> artistIds);
|
||||
List<PendingRelease> AllByArtistId(int artistId);
|
||||
List<PendingRelease> WithoutFallback();
|
||||
}
|
||||
|
@ -18,9 +18,9 @@ namespace NzbDrone.Core.Download.Pending
|
|||
{
|
||||
}
|
||||
|
||||
public void DeleteByArtistId(int artistId)
|
||||
public void DeleteByArtistIds(List<int> artistIds)
|
||||
{
|
||||
Delete(artistId);
|
||||
Delete(x => artistIds.Contains(x.ArtistId));
|
||||
}
|
||||
|
||||
public List<PendingRelease> AllByArtistId(int artistId)
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace NzbDrone.Core.Download.Pending
|
|||
}
|
||||
|
||||
public class PendingReleaseService : IPendingReleaseService,
|
||||
IHandle<ArtistDeletedEvent>,
|
||||
IHandle<ArtistsDeletedEvent>,
|
||||
IHandle<AlbumGrabbedEvent>,
|
||||
IHandle<RssSyncCompleteEvent>
|
||||
{
|
||||
|
@ -425,9 +425,9 @@ namespace NzbDrone.Core.Download.Pending
|
|||
return 1;
|
||||
}
|
||||
|
||||
public void Handle(ArtistDeletedEvent message)
|
||||
public void Handle(ArtistsDeletedEvent message)
|
||||
{
|
||||
_repository.DeleteByArtistId(message.Artist.Id);
|
||||
_repository.DeleteByArtistIds(message.Artists.Select(x => x.Id).ToList());
|
||||
}
|
||||
|
||||
public void Handle(AlbumGrabbedEvent message)
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace NzbDrone.Core.Extras.Files
|
|||
public interface IExtraFileRepository<TExtraFile> : IBasicRepository<TExtraFile>
|
||||
where TExtraFile : ExtraFile, new()
|
||||
{
|
||||
void DeleteForArtist(int artistId);
|
||||
void DeleteForArtists(List<int> artistIds);
|
||||
void DeleteForAlbum(int artistId, int albumId);
|
||||
void DeleteForTrackFile(int trackFileId);
|
||||
List<TExtraFile> GetFilesByArtist(int artistId);
|
||||
|
@ -25,9 +25,9 @@ namespace NzbDrone.Core.Extras.Files
|
|||
{
|
||||
}
|
||||
|
||||
public void DeleteForArtist(int artistId)
|
||||
public void DeleteForArtists(List<int> artistIds)
|
||||
{
|
||||
Delete(c => c.ArtistId == artistId);
|
||||
Delete(c => artistIds.Contains(c.ArtistId));
|
||||
}
|
||||
|
||||
public void DeleteForAlbum(int artistId, int albumId)
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace NzbDrone.Core.Extras.Files
|
|||
}
|
||||
|
||||
public abstract class ExtraFileService<TExtraFile> : IExtraFileService<TExtraFile>,
|
||||
IHandleAsync<ArtistDeletedEvent>,
|
||||
IHandleAsync<ArtistsDeletedEvent>,
|
||||
IHandle<TrackFileDeletedEvent>
|
||||
where TExtraFile : ExtraFile, new()
|
||||
{
|
||||
|
@ -95,10 +95,9 @@ namespace NzbDrone.Core.Extras.Files
|
|||
_repository.DeleteMany(ids);
|
||||
}
|
||||
|
||||
public void HandleAsync(ArtistDeletedEvent message)
|
||||
public void HandleAsync(ArtistsDeletedEvent message)
|
||||
{
|
||||
_logger.Debug("Deleting Extra from database for artist: {0}", message.Artist);
|
||||
_repository.DeleteForArtist(message.Artist.Id);
|
||||
_repository.DeleteForArtists(message.Artists.Select(x => x.Id).ToList());
|
||||
}
|
||||
|
||||
public void Handle(TrackFileDeletedEvent message)
|
||||
|
|
|
@ -8,7 +8,7 @@ using NzbDrone.Core.RootFolders;
|
|||
|
||||
namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
[CheckOn(typeof(ArtistDeletedEvent))]
|
||||
[CheckOn(typeof(ArtistsDeletedEvent))]
|
||||
[CheckOn(typeof(ArtistMovedEvent))]
|
||||
[CheckOn(typeof(TrackImportedEvent), CheckOnCondition.FailedOnly)]
|
||||
[CheckOn(typeof(TrackImportFailedEvent), CheckOnCondition.SuccessfulOnly)]
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace NzbDrone.Core.History
|
|||
List<History> GetByArtist(int artistId, HistoryEventType? eventType);
|
||||
List<History> GetByAlbum(int albumId, HistoryEventType? eventType);
|
||||
List<History> FindDownloadHistory(int idArtistId, QualityModel quality);
|
||||
void DeleteForArtist(int artistId);
|
||||
void DeleteForArtists(List<int> artistIds);
|
||||
List<History> Since(DateTime date, HistoryEventType? eventType);
|
||||
}
|
||||
|
||||
|
@ -97,9 +97,9 @@ namespace NzbDrone.Core.History
|
|||
allowed.Contains(h.EventType));
|
||||
}
|
||||
|
||||
public void DeleteForArtist(int artistId)
|
||||
public void DeleteForArtists(List<int> artistIds)
|
||||
{
|
||||
Delete(c => c.ArtistId == artistId);
|
||||
Delete(c => artistIds.Contains(c.ArtistId));
|
||||
}
|
||||
|
||||
protected override SqlBuilder PagedBuilder() => new SqlBuilder()
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace NzbDrone.Core.History
|
|||
IHandle<TrackFileDeletedEvent>,
|
||||
IHandle<TrackFileRenamedEvent>,
|
||||
IHandle<TrackFileRetaggedEvent>,
|
||||
IHandle<ArtistDeletedEvent>,
|
||||
IHandle<ArtistsDeletedEvent>,
|
||||
IHandle<DownloadIgnoredEvent>
|
||||
{
|
||||
private readonly IHistoryRepository _historyRepository;
|
||||
|
@ -365,9 +365,9 @@ namespace NzbDrone.Core.History
|
|||
}
|
||||
}
|
||||
|
||||
public void Handle(ArtistDeletedEvent message)
|
||||
public void Handle(ArtistsDeletedEvent message)
|
||||
{
|
||||
_historyRepository.DeleteForArtist(message.Artist.Id);
|
||||
_historyRepository.DeleteForArtists(message.Artists.Select(x => x.Id).ToList());
|
||||
}
|
||||
|
||||
public void Handle(DownloadIgnoredEvent message)
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace NzbDrone.Core.ImportLists.Exclusions
|
|||
}
|
||||
|
||||
public class ImportListExclusionService : IImportListExclusionService,
|
||||
IHandleAsync<ArtistDeletedEvent>,
|
||||
IHandleAsync<ArtistsDeletedEvent>,
|
||||
IHandleAsync<AlbumDeletedEvent>
|
||||
{
|
||||
private readonly IImportListExclusionRepository _repo;
|
||||
|
@ -72,27 +72,32 @@ namespace NzbDrone.Core.ImportLists.Exclusions
|
|||
return _repo.All().ToList();
|
||||
}
|
||||
|
||||
public void HandleAsync(ArtistDeletedEvent message)
|
||||
public void HandleAsync(ArtistsDeletedEvent message)
|
||||
{
|
||||
if (!message.AddImportListExclusion)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var existingExclusion = _repo.FindByForeignId(message.Artist.ForeignArtistId);
|
||||
var importExclusions = new List<ImportListExclusion>();
|
||||
|
||||
if (existingExclusion != null)
|
||||
foreach (var artist in message.Artists)
|
||||
{
|
||||
return;
|
||||
var existingExclusion = _repo.FindByForeignId(artist.ForeignArtistId);
|
||||
|
||||
if (existingExclusion != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
importExclusions.Add(new ImportListExclusion
|
||||
{
|
||||
ForeignId = artist.ForeignArtistId,
|
||||
Name = artist.Name
|
||||
});
|
||||
}
|
||||
|
||||
var importExclusion = new ImportListExclusion
|
||||
{
|
||||
ForeignId = message.Artist.ForeignArtistId,
|
||||
Name = message.Artist.Name
|
||||
};
|
||||
|
||||
_repo.Insert(importExclusion);
|
||||
_repo.InsertMany(importExclusions);
|
||||
}
|
||||
|
||||
public void HandleAsync(AlbumDeletedEvent message)
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace NzbDrone.Core.MediaCover
|
|||
|
||||
public class MediaCoverService :
|
||||
IHandleAsync<ArtistRefreshCompleteEvent>,
|
||||
IHandleAsync<ArtistDeletedEvent>,
|
||||
IHandleAsync<ArtistsDeletedEvent>,
|
||||
IHandleAsync<AlbumAddedEvent>,
|
||||
IHandleAsync<AlbumDeletedEvent>,
|
||||
IMapCoversToLocal
|
||||
|
@ -293,12 +293,15 @@ namespace NzbDrone.Core.MediaCover
|
|||
_eventAggregator.PublishEvent(new MediaCoversUpdatedEvent(message.Artist, updated));
|
||||
}
|
||||
|
||||
public void HandleAsync(ArtistDeletedEvent message)
|
||||
public void HandleAsync(ArtistsDeletedEvent message)
|
||||
{
|
||||
var path = GetArtistCoverPath(message.Artist.Id);
|
||||
if (_diskProvider.FolderExists(path))
|
||||
foreach (var artist in message.Artists)
|
||||
{
|
||||
_diskProvider.DeleteFolder(path, true);
|
||||
var path = GetArtistCoverPath(artist.Id);
|
||||
if (_diskProvider.FolderExists(path))
|
||||
{
|
||||
_diskProvider.DeleteFolder(path, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace NzbDrone.Core.MediaFiles
|
|||
}
|
||||
|
||||
public class MediaFileDeletionService : IDeleteMediaFiles,
|
||||
IHandleAsync<ArtistDeletedEvent>,
|
||||
IHandleAsync<ArtistsDeletedEvent>,
|
||||
IHandleAsync<AlbumDeletedEvent>,
|
||||
IHandle<TrackFileDeletedEvent>
|
||||
{
|
||||
|
@ -99,36 +99,39 @@ namespace NzbDrone.Core.MediaFiles
|
|||
_mediaFileService.Delete(trackFile, DeleteMediaFileReason.Manual);
|
||||
}
|
||||
|
||||
public void HandleAsync(ArtistDeletedEvent message)
|
||||
public void HandleAsync(ArtistsDeletedEvent message)
|
||||
{
|
||||
if (message.DeleteFiles)
|
||||
{
|
||||
var artist = message.Artist;
|
||||
var artists = message.Artists;
|
||||
var allArtists = _artistService.AllArtistPaths();
|
||||
|
||||
foreach (var s in allArtists)
|
||||
foreach (var artist in artists)
|
||||
{
|
||||
if (s.Key == artist.Id)
|
||||
foreach (var s in allArtists)
|
||||
{
|
||||
continue;
|
||||
if (s.Key == artist.Id)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (artist.Path.IsParentPath(s.Value))
|
||||
{
|
||||
_logger.Error("Artist path: '{0}' is a parent of another artist, not deleting files.", artist.Path);
|
||||
return;
|
||||
}
|
||||
|
||||
if (artist.Path.PathEquals(s.Value))
|
||||
{
|
||||
_logger.Error("Artist path: '{0}' is the same as another artist, not deleting files.", artist.Path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (artist.Path.IsParentPath(s.Value))
|
||||
if (_diskProvider.FolderExists(artist.Path))
|
||||
{
|
||||
_logger.Error("Artist path: '{0}' is a parent of another artist, not deleting files.", artist.Path);
|
||||
return;
|
||||
_recycleBinProvider.DeleteFolder(artist.Path);
|
||||
}
|
||||
|
||||
if (artist.Path.PathEquals(s.Value))
|
||||
{
|
||||
_logger.Error("Artist path: '{0}' is the same as another artist, not deleting files.", artist.Path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (_diskProvider.FolderExists(message.Artist.Path))
|
||||
{
|
||||
_recycleBinProvider.DeleteFolder(message.Artist.Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
using System.Collections.Generic;
|
||||
using NzbDrone.Common.Messaging;
|
||||
|
||||
namespace NzbDrone.Core.Music.Events
|
||||
{
|
||||
public class ArtistDeletedEvent : IEvent
|
||||
public class ArtistsDeletedEvent : IEvent
|
||||
{
|
||||
public Artist Artist { get; private set; }
|
||||
public List<Artist> Artists { get; private set; }
|
||||
public bool DeleteFiles { get; private set; }
|
||||
public bool AddImportListExclusion { get; private set; }
|
||||
|
||||
public ArtistDeletedEvent(Artist artist, bool deleteFiles, bool addImportListExclusion)
|
||||
public ArtistsDeletedEvent(List<Artist> artists, bool deleteFiles, bool addImportListExclusion)
|
||||
{
|
||||
Artist = artist;
|
||||
Artists = artists;
|
||||
DeleteFiles = deleteFiles;
|
||||
AddImportListExclusion = addImportListExclusion;
|
||||
}
|
|
@ -42,7 +42,7 @@ namespace NzbDrone.Core.Music
|
|||
}
|
||||
|
||||
public class AlbumService : IAlbumService,
|
||||
IHandle<ArtistDeletedEvent>
|
||||
IHandle<ArtistsDeletedEvent>
|
||||
{
|
||||
private readonly IAlbumRepository _albumRepository;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
|
@ -289,9 +289,10 @@ namespace NzbDrone.Core.Music
|
|||
}
|
||||
}
|
||||
|
||||
public void Handle(ArtistDeletedEvent message)
|
||||
public void Handle(ArtistsDeletedEvent message)
|
||||
{
|
||||
var albums = GetAlbumsByArtistMetadataId(message.Artist.ArtistMetadataId);
|
||||
//TODO Do this in one call instead of one for each artist?
|
||||
var albums = message.Artists.SelectMany(x => GetAlbumsByArtistMetadataId(x.ArtistMetadataId)).ToList();
|
||||
DeleteMany(albums);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace NzbDrone.Core.Music
|
|||
Artist FindByNameInexact(string title);
|
||||
List<Artist> GetCandidates(string title);
|
||||
void DeleteArtist(int artistId, bool deleteFiles, bool addImportListExclusion = false);
|
||||
void DeleteArtists(List<int> artistIds, bool deleteFiles, bool addImportListExclusion = false);
|
||||
List<Artist> GetAllArtists();
|
||||
List<Artist> AllForTag(int tagId);
|
||||
Artist UpdateArtist(Artist artist, bool publishUpdatedEvent = true);
|
||||
|
@ -80,7 +81,21 @@ namespace NzbDrone.Core.Music
|
|||
_cache.Clear();
|
||||
var artist = _artistRepository.Get(artistId);
|
||||
_artistRepository.Delete(artistId);
|
||||
_eventAggregator.PublishEvent(new ArtistDeletedEvent(artist, deleteFiles, addImportListExclusion));
|
||||
_eventAggregator.PublishEvent(new ArtistsDeletedEvent(new List<Artist> { artist }, deleteFiles, addImportListExclusion));
|
||||
}
|
||||
|
||||
public void DeleteArtists(List<int> artistIds, bool deleteFiles, bool addExclusion = false)
|
||||
{
|
||||
var artistsToDelete = _artistRepository.Get(artistIds).ToList();
|
||||
|
||||
_artistRepository.DeleteMany(artistIds);
|
||||
|
||||
_eventAggregator.PublishEvent(new ArtistsDeletedEvent(artistsToDelete, deleteFiles, addExclusion));
|
||||
|
||||
foreach (var artist in artistsToDelete)
|
||||
{
|
||||
_logger.Info("Deleted artist {0}", artist);
|
||||
}
|
||||
}
|
||||
|
||||
public Artist FindById(string foreignArtistId)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue