From 356771d139bcde4e9ca3a02a66fa462f2512cbf6 Mon Sep 17 00:00:00 2001 From: Qstick Date: Sat, 10 Dec 2022 14:19:10 -0600 Subject: [PATCH] New: Speed up mass deletes from Series Editor * New: Speed up mass deletes from Series Editor * fixup! Additional speed up using GetAllSeriesPaths vs GetAllSeries * fixup! Tests --- .../BlocklistRepositoryFixture.cs | 37 +++++++++++++ .../DownloadHistoryRepositoryFixture.cs | 53 +++++++++++++++++++ .../TrackedDownloadServiceFixture.cs | 4 +- .../HistoryTests/HistoryRepositoryFixture.cs | 44 +++++++++++++++ .../MediaFiles/MediaFileRepositoryFixture.cs | 41 ++++++++++++++ .../Blocklisting/BlocklistRepository.cs | 6 +++ .../Blocklisting/BlocklistService.cs | 4 +- .../History/DownloadHistoryRepository.cs | 6 +-- .../History/DownloadHistoryService.cs | 2 +- .../Pending/PendingReleaseRepository.cs | 6 +-- .../Download/Pending/PendingReleaseService.cs | 2 +- .../TrackedDownloadService.cs | 2 +- .../Extras/Files/ExtraFileRepository.cs | 6 +-- .../Extras/Files/ExtraFileService.cs | 4 +- .../HealthCheck/Checks/RemovedSeriesCheck.cs | 4 +- .../History/HistoryRepository.cs | 6 +-- src/NzbDrone.Core/History/HistoryService.cs | 2 +- .../Exclusions/ImportListExclusionService.cs | 25 +++++---- .../MediaCover/MediaCoverService.cs | 11 ++-- .../MediaFiles/MediaFileDeletionService.cs | 42 ++++++++------- .../MediaFiles/MediaFileRepository.cs | 6 +++ .../MediaFiles/MediaFileService.cs | 3 +- .../Notifications/NotificationService.cs | 25 +++++---- src/NzbDrone.Core/Tv/EpisodeRepository.cs | 6 +++ src/NzbDrone.Core/Tv/EpisodeService.cs | 2 +- .../Tv/Events/SeriesDeletedEvent.cs | 7 +-- src/NzbDrone.Core/Tv/SeriesService.cs | 8 +-- src/Sonarr.Api.V3/Series/SeriesController.cs | 7 ++- .../Series/SeriesEditorController.cs | 5 +- 29 files changed, 290 insertions(+), 86 deletions(-) create mode 100644 src/NzbDrone.Core.Test/Download/DownloadHistoryTests/DownloadHistoryRepositoryFixture.cs diff --git a/src/NzbDrone.Core.Test/Blocklisting/BlocklistRepositoryFixture.cs b/src/NzbDrone.Core.Test/Blocklisting/BlocklistRepositoryFixture.cs index 06e2a9a6c..5eb5102c4 100644 --- a/src/NzbDrone.Core.Test/Blocklisting/BlocklistRepositoryFixture.cs +++ b/src/NzbDrone.Core.Test/Blocklisting/BlocklistRepositoryFixture.cs @@ -1,12 +1,14 @@ using System; using System.Collections.Generic; using System.Linq; +using FizzWare.NBuilder; using FluentAssertions; using NUnit.Framework; using NzbDrone.Core.Blocklisting; using NzbDrone.Core.Languages; using NzbDrone.Core.Qualities; using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; namespace NzbDrone.Core.Test.Blocklisting { @@ -14,6 +16,8 @@ namespace NzbDrone.Core.Test.Blocklisting public class BlocklistRepositoryFixture : DbTest { private Blocklist _blocklist; + private Series _series1; + private Series _series2; [SetUp] public void Setup() @@ -27,6 +31,14 @@ namespace NzbDrone.Core.Test.Blocklisting SourceTitle = "series.title.s01e01", Date = DateTime.UtcNow }; + + _series1 = Builder.CreateNew() + .With(s => s.Id = 7) + .Build(); + + _series2 = Builder.CreateNew() + .With(s => s.Id = 8) + .Build(); } [Test] @@ -51,5 +63,30 @@ namespace NzbDrone.Core.Test.Blocklisting Subject.BlocklistedByTitle(_blocklist.SeriesId, _blocklist.SourceTitle.ToUpperInvariant()).Should().HaveCount(1); } + + [Test] + public void should_delete_blocklists_by_seriesId() + { + var blocklistItems = Builder.CreateListOfSize(5) + .TheFirst(1) + .With(c => c.SeriesId = _series2.Id) + .TheRest() + .With(c => c.SeriesId = _series1.Id) + .All() + .With(c => c.Quality = new QualityModel()) + .With(c => c.Languages = new List()) + .With(c => c.EpisodeIds = new List { 1 }) + .BuildListOfNew(); + + Db.InsertMany(blocklistItems); + + Subject.DeleteForSeriesIds(new List { _series1.Id }); + + var removedSeriesBlocklists = Subject.BlocklistedBySeries(_series1.Id); + var nonRemovedSeriesBlocklists = Subject.BlocklistedBySeries(_series2.Id); + + removedSeriesBlocklists.Should().HaveCount(0); + nonRemovedSeriesBlocklists.Should().HaveCount(1); + } } } diff --git a/src/NzbDrone.Core.Test/Download/DownloadHistoryTests/DownloadHistoryRepositoryFixture.cs b/src/NzbDrone.Core.Test/Download/DownloadHistoryTests/DownloadHistoryRepositoryFixture.cs new file mode 100644 index 000000000..c6a0658d4 --- /dev/null +++ b/src/NzbDrone.Core.Test/Download/DownloadHistoryTests/DownloadHistoryRepositoryFixture.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using System.Linq; +using FizzWare.NBuilder; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Download.History; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Core.Test.Download.DownloadHistoryTests +{ + [TestFixture] + public class DownloadHistoryRepositoryFixture : DbTest + { + private Series _series1; + private Series _series2; + + [SetUp] + public void Setup() + { + _series1 = Builder.CreateNew() + .With(s => s.Id = 7) + .Build(); + + _series2 = Builder.CreateNew() + .With(s => s.Id = 8) + .Build(); + } + + [Test] + public void should_delete_history_items_by_seriesId() + { + var items = Builder.CreateListOfSize(5) + .TheFirst(1) + .With(c => c.Id = 0) + .With(c => c.SeriesId = _series2.Id) + .TheRest() + .With(c => c.Id = 0) + .With(c => c.SeriesId = _series1.Id) + .BuildListOfNew(); + + Db.InsertMany(items); + + Subject.DeleteBySeriesIds(new List { _series1.Id }); + + var removedItems = Subject.All().Where(h => h.SeriesId == _series1.Id); + var nonRemovedItems = Subject.All().Where(h => h.SeriesId == _series2.Id); + + removedItems.Should().HaveCount(0); + nonRemovedItems.Should().HaveCount(1); + } + } +} diff --git a/src/NzbDrone.Core.Test/Download/TrackedDownloads/TrackedDownloadServiceFixture.cs b/src/NzbDrone.Core.Test/Download/TrackedDownloads/TrackedDownloadServiceFixture.cs index f3fb5ef20..c82ff848d 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; @@ -320,7 +320,7 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads .Setup(s => s.Map(It.IsAny(), It.IsAny(), It.IsAny(), null)) .Returns(default(RemoteEpisode)); - Subject.Handle(new SeriesDeletedEvent(remoteEpisode.Series, true, true)); + Subject.Handle(new SeriesDeletedEvent(new List { remoteEpisode.Series }, true, true)); var trackedDownloads = Subject.GetTrackedDownloads(); trackedDownloads.Should().HaveCount(1); diff --git a/src/NzbDrone.Core.Test/HistoryTests/HistoryRepositoryFixture.cs b/src/NzbDrone.Core.Test/HistoryTests/HistoryRepositoryFixture.cs index 79bbfc323..583fcae81 100644 --- a/src/NzbDrone.Core.Test/HistoryTests/HistoryRepositoryFixture.cs +++ b/src/NzbDrone.Core.Test/HistoryTests/HistoryRepositoryFixture.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using FizzWare.NBuilder; using FluentAssertions; using NUnit.Framework; @@ -6,12 +7,28 @@ using NzbDrone.Core.History; using NzbDrone.Core.Languages; using NzbDrone.Core.Qualities; using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; namespace NzbDrone.Core.Test.HistoryTests { [TestFixture] public class HistoryRepositoryFixture : DbTest { + private Series _series1; + private Series _series2; + + [SetUp] + public void Setup() + { + _series1 = Builder.CreateNew() + .With(s => s.Id = 7) + .Build(); + + _series2 = Builder.CreateNew() + .With(s => s.Id = 8) + .Build(); + } + [Test] public void should_read_write_dictionary() { @@ -52,5 +69,32 @@ namespace NzbDrone.Core.Test.HistoryTests downloadHistory.Should().HaveCount(1); } + + [Test] + public void should_delete_history_items_by_seriesId() + { + var items = Builder.CreateListOfSize(5) + .TheFirst(1) + .With(c => c.SeriesId = _series2.Id) + .TheRest() + .With(c => c.SeriesId = _series1.Id) + .All() + .With(c => c.Id = 0) + .With(c => c.Quality = new QualityModel(Quality.Bluray1080p)) + .With(c => c.Languages = new List { Language.English }) + .With(c => c.EventType = EpisodeHistoryEventType.Grabbed) + .BuildListOfNew(); + + Db.InsertMany(items); + + Subject.DeleteForSeries(new List { _series1.Id }); + + var dbItems = Subject.All(); + var removedItems = dbItems.Where(h => h.SeriesId == _series1.Id); + var nonRemovedItems = dbItems.Where(h => h.SeriesId == _series2.Id); + + removedItems.Should().HaveCount(0); + nonRemovedItems.Should().HaveCount(1); + } } } diff --git a/src/NzbDrone.Core.Test/MediaFiles/MediaFileRepositoryFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/MediaFileRepositoryFixture.cs index 9f7b33e6b..8b175e57f 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/MediaFileRepositoryFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/MediaFileRepositoryFixture.cs @@ -6,12 +6,28 @@ using NzbDrone.Core.Languages; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Qualities; using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; namespace NzbDrone.Core.Test.MediaFiles { [TestFixture] public class MediaFileRepositoryFixture : DbTest { + private Series _series1; + private Series _series2; + + [SetUp] + public void Setup() + { + _series1 = Builder.CreateNew() + .With(s => s.Id = 7) + .Build(); + + _series2 = Builder.CreateNew() + .With(s => s.Id = 8) + .Build(); + } + [Test] public void get_files_by_series() { @@ -31,5 +47,30 @@ namespace NzbDrone.Core.Test.MediaFiles seriesFiles.Should().HaveCount(4); seriesFiles.Should().OnlyContain(c => c.SeriesId == 12); } + + [Test] + public void should_delete_files_by_seriesId() + { + var items = Builder.CreateListOfSize(5) + .TheFirst(1) + .With(c => c.SeriesId = _series2.Id) + .TheRest() + .With(c => c.SeriesId = _series1.Id) + .All() + .With(c => c.Id = 0) + .With(c => c.Quality = new QualityModel(Quality.Bluray1080p)) + .With(c => c.Languages = new List { Language.English }) + .BuildListOfNew(); + + Db.InsertMany(items); + + Subject.DeleteForSeries(new List { _series1.Id }); + + var removedItems = Subject.GetFilesBySeries(_series1.Id); + var nonRemovedItems = Subject.GetFilesBySeries(_series2.Id); + + removedItems.Should().HaveCount(0); + nonRemovedItems.Should().HaveCount(1); + } } } diff --git a/src/NzbDrone.Core/Blocklisting/BlocklistRepository.cs b/src/NzbDrone.Core/Blocklisting/BlocklistRepository.cs index 0776fe0fd..e52edea18 100644 --- a/src/NzbDrone.Core/Blocklisting/BlocklistRepository.cs +++ b/src/NzbDrone.Core/Blocklisting/BlocklistRepository.cs @@ -10,6 +10,7 @@ namespace NzbDrone.Core.Blocklisting List BlocklistedByTitle(int seriesId, string sourceTitle); List BlocklistedByTorrentInfoHash(int seriesId, string torrentInfoHash); List BlocklistedBySeries(int seriesId); + void DeleteForSeriesIds(List seriesIds); } public class BlocklistRepository : BasicRepository, IBlocklistRepository @@ -34,6 +35,11 @@ namespace NzbDrone.Core.Blocklisting return Query(b => b.SeriesId == seriesId); } + public void DeleteForSeriesIds(List seriesIds) + { + Delete(x => seriesIds.Contains(x.SeriesId)); + } + protected override SqlBuilder PagedBuilder() => new SqlBuilder().Join((b, m) => b.SeriesId == m.Id); protected override IEnumerable PagedQuery(SqlBuilder sql) => _database.QueryJoined(sql, (bl, movie) => { diff --git a/src/NzbDrone.Core/Blocklisting/BlocklistService.cs b/src/NzbDrone.Core/Blocklisting/BlocklistService.cs index c349b09d0..a9d00c5c7 100644 --- a/src/NzbDrone.Core/Blocklisting/BlocklistService.cs +++ b/src/NzbDrone.Core/Blocklisting/BlocklistService.cs @@ -189,9 +189,7 @@ namespace NzbDrone.Core.Blocklisting public void HandleAsync(SeriesDeletedEvent message) { - var blocklisted = _blocklistRepository.BlocklistedBySeries(message.Series.Id); - - _blocklistRepository.DeleteMany(blocklisted); + _blocklistRepository.DeleteForSeriesIds(message.Series.Select(m => m.Id).ToList()); } } } diff --git a/src/NzbDrone.Core/Download/History/DownloadHistoryRepository.cs b/src/NzbDrone.Core/Download/History/DownloadHistoryRepository.cs index 4a516d1e5..f91541b9b 100644 --- a/src/NzbDrone.Core/Download/History/DownloadHistoryRepository.cs +++ b/src/NzbDrone.Core/Download/History/DownloadHistoryRepository.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Core.Download.History public interface IDownloadHistoryRepository : IBasicRepository { List FindByDownloadId(string downloadId); - void DeleteBySeriesId(int seriesId); + void DeleteBySeriesIds(List seriesIds); } public class DownloadHistoryRepository : BasicRepository, IDownloadHistoryRepository @@ -23,9 +23,9 @@ namespace NzbDrone.Core.Download.History return Query(h => h.DownloadId == downloadId).OrderByDescending(h => h.Date).ToList(); } - public void DeleteBySeriesId(int seriesId) + public void DeleteBySeriesIds(List seriesIds) { - Delete(r => r.SeriesId == seriesId); + Delete(r => seriesIds.Contains(r.SeriesId)); } } } diff --git a/src/NzbDrone.Core/Download/History/DownloadHistoryService.cs b/src/NzbDrone.Core/Download/History/DownloadHistoryService.cs index 44b49ade4..be38c04be 100644 --- a/src/NzbDrone.Core/Download/History/DownloadHistoryService.cs +++ b/src/NzbDrone.Core/Download/History/DownloadHistoryService.cs @@ -230,7 +230,7 @@ namespace NzbDrone.Core.Download.History public void Handle(SeriesDeletedEvent message) { - _repository.DeleteBySeriesId(message.Series.Id); + _repository.DeleteBySeriesIds(message.Series.Select(m => m.Id).ToList()); } } } diff --git a/src/NzbDrone.Core/Download/Pending/PendingReleaseRepository.cs b/src/NzbDrone.Core/Download/Pending/PendingReleaseRepository.cs index 33cbd16c4..5545a2f52 100644 --- a/src/NzbDrone.Core/Download/Pending/PendingReleaseRepository.cs +++ b/src/NzbDrone.Core/Download/Pending/PendingReleaseRepository.cs @@ -6,7 +6,7 @@ namespace NzbDrone.Core.Download.Pending { public interface IPendingReleaseRepository : IBasicRepository { - void DeleteBySeriesId(int seriesId); + void DeleteBySeriesIds(List seriesIds); List AllBySeriesId(int seriesId); List WithoutFallback(); } @@ -18,9 +18,9 @@ namespace NzbDrone.Core.Download.Pending { } - public void DeleteBySeriesId(int seriesId) + public void DeleteBySeriesIds(List seriesIds) { - Delete(r => r.SeriesId == seriesId); + Delete(r => seriesIds.Contains(r.SeriesId)); } public List AllBySeriesId(int seriesId) diff --git a/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs b/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs index 66c7ce7ef..263a117bc 100644 --- a/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs +++ b/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs @@ -451,7 +451,7 @@ namespace NzbDrone.Core.Download.Pending public void Handle(SeriesDeletedEvent message) { - _repository.DeleteBySeriesId(message.Series.Id); + _repository.DeleteBySeriesIds(message.Series.Select(m => m.Id).ToList()); } public void Handle(EpisodeGrabbedEvent message) diff --git a/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownloadService.cs b/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownloadService.cs index faf656863..0626f0f25 100644 --- a/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownloadService.cs +++ b/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownloadService.cs @@ -260,7 +260,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads { var cachedItems = _cache.Values.Where(t => t.RemoteEpisode?.Series != null && - t.RemoteEpisode.Series.Id == message.Series.Id) + message.Series.Any(s => s.Id == t.RemoteEpisode.Series.Id)) .ToList(); if (cachedItems.Any()) diff --git a/src/NzbDrone.Core/Extras/Files/ExtraFileRepository.cs b/src/NzbDrone.Core/Extras/Files/ExtraFileRepository.cs index 76c551d81..4b7291b03 100644 --- a/src/NzbDrone.Core/Extras/Files/ExtraFileRepository.cs +++ b/src/NzbDrone.Core/Extras/Files/ExtraFileRepository.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Core.Extras.Files public interface IExtraFileRepository : IBasicRepository where TExtraFile : ExtraFile, new() { - void DeleteForSeries(int seriesId); + void DeleteForSeriesIds(List seriesIds); void DeleteForSeason(int seriesId, int seasonNumber); void DeleteForEpisodeFile(int episodeFileId); List GetFilesBySeries(int seriesId); @@ -25,9 +25,9 @@ namespace NzbDrone.Core.Extras.Files { } - public void DeleteForSeries(int seriesId) + public void DeleteForSeriesIds(List seriesIds) { - Delete(c => c.SeriesId == seriesId); + Delete(c => seriesIds.Contains(c.SeriesId)); } public void DeleteForSeason(int seriesId, int seasonNumber) diff --git a/src/NzbDrone.Core/Extras/Files/ExtraFileService.cs b/src/NzbDrone.Core/Extras/Files/ExtraFileService.cs index ae3f50742..376d52840 100644 --- a/src/NzbDrone.Core/Extras/Files/ExtraFileService.cs +++ b/src/NzbDrone.Core/Extras/Files/ExtraFileService.cs @@ -97,8 +97,8 @@ namespace NzbDrone.Core.Extras.Files public void HandleAsync(SeriesDeletedEvent message) { - _logger.Debug("Deleting Extra from database for series: {0}", message.Series); - _repository.DeleteForSeries(message.Series.Id); + _logger.Debug("Deleting Extra from database for series: {0}", string.Join(',', message.Series)); + _repository.DeleteForSeriesIds(message.Series.Select(m => m.Id).ToList()); } public void Handle(EpisodeFileDeletedEvent message) diff --git a/src/NzbDrone.Core/HealthCheck/Checks/RemovedSeriesCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/RemovedSeriesCheck.cs index 08901e0cd..cd8d4c4cb 100644 --- a/src/NzbDrone.Core/HealthCheck/Checks/RemovedSeriesCheck.cs +++ b/src/NzbDrone.Core/HealthCheck/Checks/RemovedSeriesCheck.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Core.Tv; @@ -41,7 +41,7 @@ namespace NzbDrone.Core.HealthCheck.Checks public bool ShouldCheckOnEvent(SeriesDeletedEvent deletedEvent) { - return deletedEvent.Series.Status == SeriesStatusType.Deleted; + return deletedEvent.Series.Any(s => s.Status == SeriesStatusType.Deleted); } public bool ShouldCheckOnEvent(SeriesUpdatedEvent updatedEvent) diff --git a/src/NzbDrone.Core/History/HistoryRepository.cs b/src/NzbDrone.Core/History/HistoryRepository.cs index 3fc60c46e..b9326cd58 100644 --- a/src/NzbDrone.Core/History/HistoryRepository.cs +++ b/src/NzbDrone.Core/History/HistoryRepository.cs @@ -17,7 +17,7 @@ namespace NzbDrone.Core.History List GetBySeries(int seriesId, EpisodeHistoryEventType? eventType); List GetBySeason(int seriesId, int seasonNumber, EpisodeHistoryEventType? eventType); List FindDownloadHistory(int idSeriesId, QualityModel quality); - void DeleteForSeries(int seriesId); + void DeleteForSeries(List seriesIds); List Since(DateTime date, EpisodeHistoryEventType? eventType); } @@ -100,9 +100,9 @@ namespace NzbDrone.Core.History .ToList(); } - public void DeleteForSeries(int seriesId) + public void DeleteForSeries(List seriesIds) { - Delete(c => c.SeriesId == seriesId); + Delete(c => seriesIds.Contains(c.SeriesId)); } protected override SqlBuilder PagedBuilder() => new SqlBuilder() diff --git a/src/NzbDrone.Core/History/HistoryService.cs b/src/NzbDrone.Core/History/HistoryService.cs index fa1b6269d..948f7c8ae 100644 --- a/src/NzbDrone.Core/History/HistoryService.cs +++ b/src/NzbDrone.Core/History/HistoryService.cs @@ -343,7 +343,7 @@ namespace NzbDrone.Core.History public void Handle(SeriesDeletedEvent message) { - _historyRepository.DeleteForSeries(message.Series.Id); + _historyRepository.DeleteForSeries(message.Series.Select(m => m.Id).ToList()); } public List Since(DateTime date, EpisodeHistoryEventType? eventType) diff --git a/src/NzbDrone.Core/ImportLists/Exclusions/ImportListExclusionService.cs b/src/NzbDrone.Core/ImportLists/Exclusions/ImportListExclusionService.cs index 366c32293..09871fef3 100644 --- a/src/NzbDrone.Core/ImportLists/Exclusions/ImportListExclusionService.cs +++ b/src/NzbDrone.Core/ImportLists/Exclusions/ImportListExclusionService.cs @@ -61,20 +61,25 @@ namespace NzbDrone.Core.ImportLists.Exclusions return; } - var existingExclusion = _repo.FindByTvdbId(message.Series.TvdbId); + var exclusionsToAdd = new List(); - if (existingExclusion != null) + foreach (var series in message.Series.DistinctBy(s => s.TvdbId)) { - return; + var existingExclusion = _repo.FindByTvdbId(series.TvdbId); + + if (existingExclusion != null) + { + continue; + } + + exclusionsToAdd.Add(new ImportListExclusion + { + TvdbId = series.TvdbId, + Title = series.Title + }); } - var importExclusion = new ImportListExclusion - { - TvdbId = message.Series.TvdbId, - Title = message.Series.Title - }; - - _repo.Insert(importExclusion); + _repo.InsertMany(exclusionsToAdd); } } } diff --git a/src/NzbDrone.Core/MediaCover/MediaCoverService.cs b/src/NzbDrone.Core/MediaCover/MediaCoverService.cs index 0da3ae067..d45ab6161 100644 --- a/src/NzbDrone.Core/MediaCover/MediaCoverService.cs +++ b/src/NzbDrone.Core/MediaCover/MediaCoverService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Net; @@ -217,10 +217,13 @@ namespace NzbDrone.Core.MediaCover public void HandleAsync(SeriesDeletedEvent message) { - var path = GetSeriesCoverPath(message.Series.Id); - if (_diskProvider.FolderExists(path)) + foreach (var series in message.Series) { - _diskProvider.DeleteFolder(path, true); + var path = GetSeriesCoverPath(series.Id); + if (_diskProvider.FolderExists(path)) + { + _diskProvider.DeleteFolder(path, true); + } } } } diff --git a/src/NzbDrone.Core/MediaFiles/MediaFileDeletionService.cs b/src/NzbDrone.Core/MediaFiles/MediaFileDeletionService.cs index a80437ace..064f15157 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaFileDeletionService.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaFileDeletionService.cs @@ -92,35 +92,37 @@ namespace NzbDrone.Core.MediaFiles { if (message.DeleteFiles) { - var series = message.Series; - var allSeries = _seriesService.GetAllSeries(); + var allSeries = _seriesService.GetAllSeriesPaths(); - foreach (var s in allSeries) + foreach (var series in message.Series) { - if (s.Id == series.Id) + foreach (var s in allSeries) { - continue; + if (s.Key == series.Id) + { + continue; + } + + if (series.Path.IsParentPath(s.Value)) + { + _logger.Error("Series path: '{0}' is a parent of another series, not deleting files.", series.Path); + return; + } + + if (series.Path.PathEquals(s.Value)) + { + _logger.Error("Series path: '{0}' is the same as another series, not deleting files.", series.Path); + return; + } } - if (series.Path.IsParentPath(s.Path)) + if (_diskProvider.FolderExists(series.Path)) { - _logger.Error("Series path: '{0}' is a parent of another series, not deleting files.", series.Path); - return; + _recycleBinProvider.DeleteFolder(series.Path); } - if (series.Path.PathEquals(s.Path)) - { - _logger.Error("Series path: '{0}' is the same as another series, not deleting files.", series.Path); - return; - } + _eventAggregator.PublishEvent(new DeleteCompletedEvent()); } - - if (_diskProvider.FolderExists(message.Series.Path)) - { - _recycleBinProvider.DeleteFolder(message.Series.Path); - } - - _eventAggregator.PublishEvent(new DeleteCompletedEvent()); } } diff --git a/src/NzbDrone.Core/MediaFiles/MediaFileRepository.cs b/src/NzbDrone.Core/MediaFiles/MediaFileRepository.cs index 9e7c15724..aba43c8fa 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaFileRepository.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaFileRepository.cs @@ -11,6 +11,7 @@ namespace NzbDrone.Core.MediaFiles List GetFilesBySeason(int seriesId, int seasonNumber); List GetFilesWithoutMediaInfo(); List GetFilesWithRelativePath(int seriesId, string relativePath); + void DeleteForSeries(List seriesIds); } public class MediaFileRepository : BasicRepository, IMediaFileRepository @@ -40,5 +41,10 @@ namespace NzbDrone.Core.MediaFiles return Query(c => c.SeriesId == seriesId && c.RelativePath == relativePath) .ToList(); } + + public void DeleteForSeries(List seriesIds) + { + Delete(x => seriesIds.Contains(x.SeriesId)); + } } } diff --git a/src/NzbDrone.Core/MediaFiles/MediaFileService.cs b/src/NzbDrone.Core/MediaFiles/MediaFileService.cs index 13b61974c..6e2c78d46 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaFileService.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaFileService.cs @@ -110,8 +110,7 @@ namespace NzbDrone.Core.MediaFiles public void HandleAsync(SeriesDeletedEvent message) { - var files = GetFilesBySeries(message.Series.Id); - _mediaFileRepository.DeleteMany(files); + _mediaFileRepository.DeleteForSeries(message.Series.Select(s => s.Id).ToList()); } public static List FilterExistingFiles(List files, List seriesFiles, Series series) diff --git a/src/NzbDrone.Core/Notifications/NotificationService.cs b/src/NzbDrone.Core/Notifications/NotificationService.cs index 2557ef019..d6dfee81b 100644 --- a/src/NzbDrone.Core/Notifications/NotificationService.cs +++ b/src/NzbDrone.Core/Notifications/NotificationService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using NLog; @@ -252,20 +252,23 @@ namespace NzbDrone.Core.Notifications public void Handle(SeriesDeletedEvent message) { - var deleteMessage = new SeriesDeleteMessage(message.Series, message.DeleteFiles); - - foreach (var notification in _notificationFactory.OnSeriesDeleteEnabled()) + foreach (var series in message.Series) { - try + var deleteMessage = new SeriesDeleteMessage(series, message.DeleteFiles); + + foreach (var notification in _notificationFactory.OnSeriesDeleteEnabled()) { - if (ShouldHandleSeries(notification.Definition, deleteMessage.Series)) + try { - notification.OnSeriesDelete(deleteMessage); + if (ShouldHandleSeries(notification.Definition, deleteMessage.Series)) + { + notification.OnSeriesDelete(deleteMessage); + } + } + catch (Exception ex) + { + _logger.Warn(ex, "Unable to send OnDelete notification to: " + notification.Definition.Name); } - } - catch (Exception ex) - { - _logger.Warn(ex, "Unable to send OnDelete notification to: " + notification.Definition.Name); } } } diff --git a/src/NzbDrone.Core/Tv/EpisodeRepository.cs b/src/NzbDrone.Core/Tv/EpisodeRepository.cs index 7c3ecfd44..b8d06cdcf 100644 --- a/src/NzbDrone.Core/Tv/EpisodeRepository.cs +++ b/src/NzbDrone.Core/Tv/EpisodeRepository.cs @@ -18,6 +18,7 @@ namespace NzbDrone.Core.Tv List Find(int seriesId, string date); List GetEpisodes(int seriesId); List GetEpisodes(int seriesId, int seasonNumber); + List GetEpisodesBySeriesIds(List seriesIds); List GetEpisodesBySceneSeason(int seriesId, int sceneSeasonNumber); List GetEpisodeByFileId(int fileId); List EpisodesWithFiles(int seriesId); @@ -77,6 +78,11 @@ namespace NzbDrone.Core.Tv return Query(s => s.SeriesId == seriesId && s.SeasonNumber == seasonNumber).ToList(); } + public List GetEpisodesBySeriesIds(List seriesIds) + { + return Query(s => seriesIds.Contains(s.SeriesId)).ToList(); + } + public List GetEpisodesBySceneSeason(int seriesId, int seasonNumber) { return Query(s => s.SeriesId == seriesId && s.SceneSeasonNumber == seasonNumber).ToList(); diff --git a/src/NzbDrone.Core/Tv/EpisodeService.cs b/src/NzbDrone.Core/Tv/EpisodeService.cs index 2221ff452..255fa6255 100644 --- a/src/NzbDrone.Core/Tv/EpisodeService.cs +++ b/src/NzbDrone.Core/Tv/EpisodeService.cs @@ -211,7 +211,7 @@ namespace NzbDrone.Core.Tv public void HandleAsync(SeriesDeletedEvent message) { - var episodes = GetEpisodeBySeries(message.Series.Id); + var episodes = _episodeRepository.GetEpisodesBySeriesIds(message.Series.Select(s => s.Id).ToList()); _episodeRepository.DeleteMany(episodes); } diff --git a/src/NzbDrone.Core/Tv/Events/SeriesDeletedEvent.cs b/src/NzbDrone.Core/Tv/Events/SeriesDeletedEvent.cs index bdc9f89ca..38130a511 100644 --- a/src/NzbDrone.Core/Tv/Events/SeriesDeletedEvent.cs +++ b/src/NzbDrone.Core/Tv/Events/SeriesDeletedEvent.cs @@ -1,14 +1,15 @@ -using NzbDrone.Common.Messaging; +using System.Collections.Generic; +using NzbDrone.Common.Messaging; namespace NzbDrone.Core.Tv.Events { public class SeriesDeletedEvent : IEvent { - public Series Series { get; private set; } + public List Series { get; private set; } public bool DeleteFiles { get; private set; } public bool AddImportListExclusion { get; private set; } - public SeriesDeletedEvent(Series series, bool deleteFiles, bool addImportListExclusion) + public SeriesDeletedEvent(List series, bool deleteFiles, bool addImportListExclusion) { Series = series; DeleteFiles = deleteFiles; diff --git a/src/NzbDrone.Core/Tv/SeriesService.cs b/src/NzbDrone.Core/Tv/SeriesService.cs index 2bb4976ee..24cf86490 100644 --- a/src/NzbDrone.Core/Tv/SeriesService.cs +++ b/src/NzbDrone.Core/Tv/SeriesService.cs @@ -22,7 +22,7 @@ namespace NzbDrone.Core.Tv Series FindByTitle(string title, int year); Series FindByTitleInexact(string title); Series FindByPath(string path); - void DeleteSeries(int seriesId, bool deleteFiles, bool addImportListExclusion); + void DeleteSeries(List seriesIds, bool deleteFiles, bool addImportListExclusion); List GetAllSeries(); List AllSeriesTvdbIds(); Dictionary GetAllSeriesPaths(); @@ -149,10 +149,10 @@ namespace NzbDrone.Core.Tv return _seriesRepository.FindByTitle(title.CleanSeriesTitle(), year); } - public void DeleteSeries(int seriesId, bool deleteFiles, bool addImportListExclusion) + public void DeleteSeries(List seriesIds, bool deleteFiles, bool addImportListExclusion) { - var series = _seriesRepository.Get(seriesId); - _seriesRepository.Delete(seriesId); + var series = _seriesRepository.Get(seriesIds).ToList(); + _seriesRepository.DeleteMany(seriesIds); _eventAggregator.PublishEvent(new SeriesDeletedEvent(series, deleteFiles, addImportListExclusion)); } diff --git a/src/Sonarr.Api.V3/Series/SeriesController.cs b/src/Sonarr.Api.V3/Series/SeriesController.cs index 9155f6c8b..344a084c0 100644 --- a/src/Sonarr.Api.V3/Series/SeriesController.cs +++ b/src/Sonarr.Api.V3/Series/SeriesController.cs @@ -172,7 +172,7 @@ namespace Sonarr.Api.V3.Series var deleteFiles = Request.GetBooleanQueryParameter("deleteFiles"); var addImportListExclusion = Request.GetBooleanQueryParameter("addImportListExclusion"); - _seriesService.DeleteSeries(id, deleteFiles, addImportListExclusion); + _seriesService.DeleteSeries(new List { id }, deleteFiles, addImportListExclusion); } private SeriesResource GetSeriesResource(NzbDrone.Core.Tv.Series series, bool includeSeasonImages) @@ -292,7 +292,10 @@ namespace Sonarr.Api.V3.Series [NonAction] public void Handle(SeriesDeletedEvent message) { - BroadcastResourceChange(ModelAction.Deleted, message.Series.ToResource()); + foreach (var series in message.Series) + { + BroadcastResourceChange(ModelAction.Deleted, series.ToResource()); + } } [NonAction] diff --git a/src/Sonarr.Api.V3/Series/SeriesEditorController.cs b/src/Sonarr.Api.V3/Series/SeriesEditorController.cs index 1af3f0b4c..99927887b 100644 --- a/src/Sonarr.Api.V3/Series/SeriesEditorController.cs +++ b/src/Sonarr.Api.V3/Series/SeriesEditorController.cs @@ -94,10 +94,7 @@ namespace Sonarr.Api.V3.Series [HttpDelete] public object DeleteSeries([FromBody] SeriesEditorResource resource) { - foreach (var seriesId in resource.SeriesIds) - { - _seriesService.DeleteSeries(seriesId, resource.DeleteFiles, resource.AddImportListExclusion); - } + _seriesService.DeleteSeries(resource.SeriesIds, resource.DeleteFiles, resource.AddImportListExclusion); return new { }; }