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
This commit is contained in:
Qstick 2022-12-10 14:19:10 -06:00 committed by GitHub
parent d08f33ae21
commit 356771d139
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 290 additions and 86 deletions

View File

@ -1,12 +1,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Blocklisting; using NzbDrone.Core.Blocklisting;
using NzbDrone.Core.Languages; using NzbDrone.Core.Languages;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.Blocklisting namespace NzbDrone.Core.Test.Blocklisting
{ {
@ -14,6 +16,8 @@ namespace NzbDrone.Core.Test.Blocklisting
public class BlocklistRepositoryFixture : DbTest<BlocklistRepository, Blocklist> public class BlocklistRepositoryFixture : DbTest<BlocklistRepository, Blocklist>
{ {
private Blocklist _blocklist; private Blocklist _blocklist;
private Series _series1;
private Series _series2;
[SetUp] [SetUp]
public void Setup() public void Setup()
@ -27,6 +31,14 @@ namespace NzbDrone.Core.Test.Blocklisting
SourceTitle = "series.title.s01e01", SourceTitle = "series.title.s01e01",
Date = DateTime.UtcNow Date = DateTime.UtcNow
}; };
_series1 = Builder<Series>.CreateNew()
.With(s => s.Id = 7)
.Build();
_series2 = Builder<Series>.CreateNew()
.With(s => s.Id = 8)
.Build();
} }
[Test] [Test]
@ -51,5 +63,30 @@ namespace NzbDrone.Core.Test.Blocklisting
Subject.BlocklistedByTitle(_blocklist.SeriesId, _blocklist.SourceTitle.ToUpperInvariant()).Should().HaveCount(1); Subject.BlocklistedByTitle(_blocklist.SeriesId, _blocklist.SourceTitle.ToUpperInvariant()).Should().HaveCount(1);
} }
[Test]
public void should_delete_blocklists_by_seriesId()
{
var blocklistItems = Builder<Blocklist>.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<Language>())
.With(c => c.EpisodeIds = new List<int> { 1 })
.BuildListOfNew();
Db.InsertMany(blocklistItems);
Subject.DeleteForSeriesIds(new List<int> { _series1.Id });
var removedSeriesBlocklists = Subject.BlocklistedBySeries(_series1.Id);
var nonRemovedSeriesBlocklists = Subject.BlocklistedBySeries(_series2.Id);
removedSeriesBlocklists.Should().HaveCount(0);
nonRemovedSeriesBlocklists.Should().HaveCount(1);
}
} }
} }

View File

@ -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<DownloadHistoryRepository, DownloadHistory>
{
private Series _series1;
private Series _series2;
[SetUp]
public void Setup()
{
_series1 = Builder<Series>.CreateNew()
.With(s => s.Id = 7)
.Build();
_series2 = Builder<Series>.CreateNew()
.With(s => s.Id = 8)
.Build();
}
[Test]
public void should_delete_history_items_by_seriesId()
{
var items = Builder<DownloadHistory>.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<int> { _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);
}
}
}

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;
@ -320,7 +320,7 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads
.Setup(s => s.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), It.IsAny<int>(), null)) .Setup(s => s.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), It.IsAny<int>(), null))
.Returns(default(RemoteEpisode)); .Returns(default(RemoteEpisode));
Subject.Handle(new SeriesDeletedEvent(remoteEpisode.Series, true, true)); Subject.Handle(new SeriesDeletedEvent(new List<Series> { remoteEpisode.Series }, true, true));
var trackedDownloads = Subject.GetTrackedDownloads(); var trackedDownloads = Subject.GetTrackedDownloads();
trackedDownloads.Should().HaveCount(1); trackedDownloads.Should().HaveCount(1);

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
@ -6,12 +7,28 @@ using NzbDrone.Core.History;
using NzbDrone.Core.Languages; using NzbDrone.Core.Languages;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.HistoryTests namespace NzbDrone.Core.Test.HistoryTests
{ {
[TestFixture] [TestFixture]
public class HistoryRepositoryFixture : DbTest<HistoryRepository, EpisodeHistory> public class HistoryRepositoryFixture : DbTest<HistoryRepository, EpisodeHistory>
{ {
private Series _series1;
private Series _series2;
[SetUp]
public void Setup()
{
_series1 = Builder<Series>.CreateNew()
.With(s => s.Id = 7)
.Build();
_series2 = Builder<Series>.CreateNew()
.With(s => s.Id = 8)
.Build();
}
[Test] [Test]
public void should_read_write_dictionary() public void should_read_write_dictionary()
{ {
@ -52,5 +69,32 @@ namespace NzbDrone.Core.Test.HistoryTests
downloadHistory.Should().HaveCount(1); downloadHistory.Should().HaveCount(1);
} }
[Test]
public void should_delete_history_items_by_seriesId()
{
var items = Builder<EpisodeHistory>.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> { Language.English })
.With(c => c.EventType = EpisodeHistoryEventType.Grabbed)
.BuildListOfNew();
Db.InsertMany(items);
Subject.DeleteForSeries(new List<int> { _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);
}
} }
} }

View File

@ -6,12 +6,28 @@ using NzbDrone.Core.Languages;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.MediaFiles namespace NzbDrone.Core.Test.MediaFiles
{ {
[TestFixture] [TestFixture]
public class MediaFileRepositoryFixture : DbTest<MediaFileRepository, EpisodeFile> public class MediaFileRepositoryFixture : DbTest<MediaFileRepository, EpisodeFile>
{ {
private Series _series1;
private Series _series2;
[SetUp]
public void Setup()
{
_series1 = Builder<Series>.CreateNew()
.With(s => s.Id = 7)
.Build();
_series2 = Builder<Series>.CreateNew()
.With(s => s.Id = 8)
.Build();
}
[Test] [Test]
public void get_files_by_series() public void get_files_by_series()
{ {
@ -31,5 +47,30 @@ namespace NzbDrone.Core.Test.MediaFiles
seriesFiles.Should().HaveCount(4); seriesFiles.Should().HaveCount(4);
seriesFiles.Should().OnlyContain(c => c.SeriesId == 12); seriesFiles.Should().OnlyContain(c => c.SeriesId == 12);
} }
[Test]
public void should_delete_files_by_seriesId()
{
var items = Builder<EpisodeFile>.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> { Language.English })
.BuildListOfNew();
Db.InsertMany(items);
Subject.DeleteForSeries(new List<int> { _series1.Id });
var removedItems = Subject.GetFilesBySeries(_series1.Id);
var nonRemovedItems = Subject.GetFilesBySeries(_series2.Id);
removedItems.Should().HaveCount(0);
nonRemovedItems.Should().HaveCount(1);
}
} }
} }

View File

@ -10,6 +10,7 @@ namespace NzbDrone.Core.Blocklisting
List<Blocklist> BlocklistedByTitle(int seriesId, string sourceTitle); List<Blocklist> BlocklistedByTitle(int seriesId, string sourceTitle);
List<Blocklist> BlocklistedByTorrentInfoHash(int seriesId, string torrentInfoHash); List<Blocklist> BlocklistedByTorrentInfoHash(int seriesId, string torrentInfoHash);
List<Blocklist> BlocklistedBySeries(int seriesId); List<Blocklist> BlocklistedBySeries(int seriesId);
void DeleteForSeriesIds(List<int> seriesIds);
} }
public class BlocklistRepository : BasicRepository<Blocklist>, IBlocklistRepository public class BlocklistRepository : BasicRepository<Blocklist>, IBlocklistRepository
@ -34,6 +35,11 @@ namespace NzbDrone.Core.Blocklisting
return Query(b => b.SeriesId == seriesId); return Query(b => b.SeriesId == seriesId);
} }
public void DeleteForSeriesIds(List<int> seriesIds)
{
Delete(x => seriesIds.Contains(x.SeriesId));
}
protected override SqlBuilder PagedBuilder() => new SqlBuilder().Join<Blocklist, Series>((b, m) => b.SeriesId == m.Id); protected override SqlBuilder PagedBuilder() => new SqlBuilder().Join<Blocklist, Series>((b, m) => b.SeriesId == m.Id);
protected override IEnumerable<Blocklist> PagedQuery(SqlBuilder sql) => _database.QueryJoined<Blocklist, Series>(sql, (bl, movie) => protected override IEnumerable<Blocklist> PagedQuery(SqlBuilder sql) => _database.QueryJoined<Blocklist, Series>(sql, (bl, movie) =>
{ {

View File

@ -189,9 +189,7 @@ namespace NzbDrone.Core.Blocklisting
public void HandleAsync(SeriesDeletedEvent message) public void HandleAsync(SeriesDeletedEvent message)
{ {
var blocklisted = _blocklistRepository.BlocklistedBySeries(message.Series.Id); _blocklistRepository.DeleteForSeriesIds(message.Series.Select(m => m.Id).ToList());
_blocklistRepository.DeleteMany(blocklisted);
} }
} }
} }

View File

@ -8,7 +8,7 @@ namespace NzbDrone.Core.Download.History
public interface IDownloadHistoryRepository : IBasicRepository<DownloadHistory> public interface IDownloadHistoryRepository : IBasicRepository<DownloadHistory>
{ {
List<DownloadHistory> FindByDownloadId(string downloadId); List<DownloadHistory> FindByDownloadId(string downloadId);
void DeleteBySeriesId(int seriesId); void DeleteBySeriesIds(List<int> seriesIds);
} }
public class DownloadHistoryRepository : BasicRepository<DownloadHistory>, IDownloadHistoryRepository public class DownloadHistoryRepository : BasicRepository<DownloadHistory>, IDownloadHistoryRepository
@ -23,9 +23,9 @@ namespace NzbDrone.Core.Download.History
return Query(h => h.DownloadId == downloadId).OrderByDescending(h => h.Date).ToList(); return Query(h => h.DownloadId == downloadId).OrderByDescending(h => h.Date).ToList();
} }
public void DeleteBySeriesId(int seriesId) public void DeleteBySeriesIds(List<int> seriesIds)
{ {
Delete(r => r.SeriesId == seriesId); Delete(r => seriesIds.Contains(r.SeriesId));
} }
} }
} }

View File

@ -230,7 +230,7 @@ namespace NzbDrone.Core.Download.History
public void Handle(SeriesDeletedEvent message) public void Handle(SeriesDeletedEvent message)
{ {
_repository.DeleteBySeriesId(message.Series.Id); _repository.DeleteBySeriesIds(message.Series.Select(m => m.Id).ToList());
} }
} }
} }

View File

@ -6,7 +6,7 @@ namespace NzbDrone.Core.Download.Pending
{ {
public interface IPendingReleaseRepository : IBasicRepository<PendingRelease> public interface IPendingReleaseRepository : IBasicRepository<PendingRelease>
{ {
void DeleteBySeriesId(int seriesId); void DeleteBySeriesIds(List<int> seriesIds);
List<PendingRelease> AllBySeriesId(int seriesId); List<PendingRelease> AllBySeriesId(int seriesId);
List<PendingRelease> WithoutFallback(); List<PendingRelease> WithoutFallback();
} }
@ -18,9 +18,9 @@ namespace NzbDrone.Core.Download.Pending
{ {
} }
public void DeleteBySeriesId(int seriesId) public void DeleteBySeriesIds(List<int> seriesIds)
{ {
Delete(r => r.SeriesId == seriesId); Delete(r => seriesIds.Contains(r.SeriesId));
} }
public List<PendingRelease> AllBySeriesId(int seriesId) public List<PendingRelease> AllBySeriesId(int seriesId)

View File

@ -451,7 +451,7 @@ namespace NzbDrone.Core.Download.Pending
public void Handle(SeriesDeletedEvent message) public void Handle(SeriesDeletedEvent message)
{ {
_repository.DeleteBySeriesId(message.Series.Id); _repository.DeleteBySeriesIds(message.Series.Select(m => m.Id).ToList());
} }
public void Handle(EpisodeGrabbedEvent message) public void Handle(EpisodeGrabbedEvent message)

View File

@ -260,7 +260,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
{ {
var cachedItems = _cache.Values.Where(t => var cachedItems = _cache.Values.Where(t =>
t.RemoteEpisode?.Series != null && t.RemoteEpisode?.Series != null &&
t.RemoteEpisode.Series.Id == message.Series.Id) message.Series.Any(s => s.Id == t.RemoteEpisode.Series.Id))
.ToList(); .ToList();
if (cachedItems.Any()) if (cachedItems.Any())

View File

@ -8,7 +8,7 @@ namespace NzbDrone.Core.Extras.Files
public interface IExtraFileRepository<TExtraFile> : IBasicRepository<TExtraFile> public interface IExtraFileRepository<TExtraFile> : IBasicRepository<TExtraFile>
where TExtraFile : ExtraFile, new() where TExtraFile : ExtraFile, new()
{ {
void DeleteForSeries(int seriesId); void DeleteForSeriesIds(List<int> seriesIds);
void DeleteForSeason(int seriesId, int seasonNumber); void DeleteForSeason(int seriesId, int seasonNumber);
void DeleteForEpisodeFile(int episodeFileId); void DeleteForEpisodeFile(int episodeFileId);
List<TExtraFile> GetFilesBySeries(int seriesId); List<TExtraFile> GetFilesBySeries(int seriesId);
@ -25,9 +25,9 @@ namespace NzbDrone.Core.Extras.Files
{ {
} }
public void DeleteForSeries(int seriesId) public void DeleteForSeriesIds(List<int> seriesIds)
{ {
Delete(c => c.SeriesId == seriesId); Delete(c => seriesIds.Contains(c.SeriesId));
} }
public void DeleteForSeason(int seriesId, int seasonNumber) public void DeleteForSeason(int seriesId, int seasonNumber)

View File

@ -97,8 +97,8 @@ namespace NzbDrone.Core.Extras.Files
public void HandleAsync(SeriesDeletedEvent message) public void HandleAsync(SeriesDeletedEvent message)
{ {
_logger.Debug("Deleting Extra from database for series: {0}", message.Series); _logger.Debug("Deleting Extra from database for series: {0}", string.Join(',', message.Series));
_repository.DeleteForSeries(message.Series.Id); _repository.DeleteForSeriesIds(message.Series.Select(m => m.Id).ToList());
} }
public void Handle(EpisodeFileDeletedEvent message) public void Handle(EpisodeFileDeletedEvent message)

View File

@ -1,4 +1,4 @@
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
@ -41,7 +41,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
public bool ShouldCheckOnEvent(SeriesDeletedEvent deletedEvent) 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) public bool ShouldCheckOnEvent(SeriesUpdatedEvent updatedEvent)

View File

@ -17,7 +17,7 @@ namespace NzbDrone.Core.History
List<EpisodeHistory> GetBySeries(int seriesId, EpisodeHistoryEventType? eventType); List<EpisodeHistory> GetBySeries(int seriesId, EpisodeHistoryEventType? eventType);
List<EpisodeHistory> GetBySeason(int seriesId, int seasonNumber, EpisodeHistoryEventType? eventType); List<EpisodeHistory> GetBySeason(int seriesId, int seasonNumber, EpisodeHistoryEventType? eventType);
List<EpisodeHistory> FindDownloadHistory(int idSeriesId, QualityModel quality); List<EpisodeHistory> FindDownloadHistory(int idSeriesId, QualityModel quality);
void DeleteForSeries(int seriesId); void DeleteForSeries(List<int> seriesIds);
List<EpisodeHistory> Since(DateTime date, EpisodeHistoryEventType? eventType); List<EpisodeHistory> Since(DateTime date, EpisodeHistoryEventType? eventType);
} }
@ -100,9 +100,9 @@ namespace NzbDrone.Core.History
.ToList(); .ToList();
} }
public void DeleteForSeries(int seriesId) public void DeleteForSeries(List<int> seriesIds)
{ {
Delete(c => c.SeriesId == seriesId); Delete(c => seriesIds.Contains(c.SeriesId));
} }
protected override SqlBuilder PagedBuilder() => new SqlBuilder() protected override SqlBuilder PagedBuilder() => new SqlBuilder()

View File

@ -343,7 +343,7 @@ namespace NzbDrone.Core.History
public void Handle(SeriesDeletedEvent message) public void Handle(SeriesDeletedEvent message)
{ {
_historyRepository.DeleteForSeries(message.Series.Id); _historyRepository.DeleteForSeries(message.Series.Select(m => m.Id).ToList());
} }
public List<EpisodeHistory> Since(DateTime date, EpisodeHistoryEventType? eventType) public List<EpisodeHistory> Since(DateTime date, EpisodeHistoryEventType? eventType)

View File

@ -61,20 +61,25 @@ namespace NzbDrone.Core.ImportLists.Exclusions
return; return;
} }
var existingExclusion = _repo.FindByTvdbId(message.Series.TvdbId); var exclusionsToAdd = new List<ImportListExclusion>();
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 _repo.InsertMany(exclusionsToAdd);
{
TvdbId = message.Series.TvdbId,
Title = message.Series.Title
};
_repo.Insert(importExclusion);
} }
} }
} }

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
@ -217,10 +217,13 @@ namespace NzbDrone.Core.MediaCover
public void HandleAsync(SeriesDeletedEvent message) public void HandleAsync(SeriesDeletedEvent message)
{ {
var path = GetSeriesCoverPath(message.Series.Id); foreach (var series in message.Series)
if (_diskProvider.FolderExists(path))
{ {
_diskProvider.DeleteFolder(path, true); var path = GetSeriesCoverPath(series.Id);
if (_diskProvider.FolderExists(path))
{
_diskProvider.DeleteFolder(path, true);
}
} }
} }
} }

View File

@ -92,35 +92,37 @@ namespace NzbDrone.Core.MediaFiles
{ {
if (message.DeleteFiles) if (message.DeleteFiles)
{ {
var series = message.Series; var allSeries = _seriesService.GetAllSeriesPaths();
var allSeries = _seriesService.GetAllSeries();
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); _recycleBinProvider.DeleteFolder(series.Path);
return;
} }
if (series.Path.PathEquals(s.Path)) _eventAggregator.PublishEvent(new DeleteCompletedEvent());
{
_logger.Error("Series path: '{0}' is the same as another series, not deleting files.", series.Path);
return;
}
} }
if (_diskProvider.FolderExists(message.Series.Path))
{
_recycleBinProvider.DeleteFolder(message.Series.Path);
}
_eventAggregator.PublishEvent(new DeleteCompletedEvent());
} }
} }

View File

@ -11,6 +11,7 @@ namespace NzbDrone.Core.MediaFiles
List<EpisodeFile> GetFilesBySeason(int seriesId, int seasonNumber); List<EpisodeFile> GetFilesBySeason(int seriesId, int seasonNumber);
List<EpisodeFile> GetFilesWithoutMediaInfo(); List<EpisodeFile> GetFilesWithoutMediaInfo();
List<EpisodeFile> GetFilesWithRelativePath(int seriesId, string relativePath); List<EpisodeFile> GetFilesWithRelativePath(int seriesId, string relativePath);
void DeleteForSeries(List<int> seriesIds);
} }
public class MediaFileRepository : BasicRepository<EpisodeFile>, IMediaFileRepository public class MediaFileRepository : BasicRepository<EpisodeFile>, IMediaFileRepository
@ -40,5 +41,10 @@ namespace NzbDrone.Core.MediaFiles
return Query(c => c.SeriesId == seriesId && c.RelativePath == relativePath) return Query(c => c.SeriesId == seriesId && c.RelativePath == relativePath)
.ToList(); .ToList();
} }
public void DeleteForSeries(List<int> seriesIds)
{
Delete(x => seriesIds.Contains(x.SeriesId));
}
} }
} }

View File

@ -110,8 +110,7 @@ namespace NzbDrone.Core.MediaFiles
public void HandleAsync(SeriesDeletedEvent message) public void HandleAsync(SeriesDeletedEvent message)
{ {
var files = GetFilesBySeries(message.Series.Id); _mediaFileRepository.DeleteForSeries(message.Series.Select(s => s.Id).ToList());
_mediaFileRepository.DeleteMany(files);
} }
public static List<string> FilterExistingFiles(List<string> files, List<EpisodeFile> seriesFiles, Series series) public static List<string> FilterExistingFiles(List<string> files, List<EpisodeFile> seriesFiles, Series series)

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NLog; using NLog;
@ -252,20 +252,23 @@ namespace NzbDrone.Core.Notifications
public void Handle(SeriesDeletedEvent message) public void Handle(SeriesDeletedEvent message)
{ {
var deleteMessage = new SeriesDeleteMessage(message.Series, message.DeleteFiles); foreach (var series in message.Series)
foreach (var notification in _notificationFactory.OnSeriesDeleteEnabled())
{ {
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);
} }
} }
} }

View File

@ -18,6 +18,7 @@ namespace NzbDrone.Core.Tv
List<Episode> Find(int seriesId, string date); List<Episode> Find(int seriesId, string date);
List<Episode> GetEpisodes(int seriesId); List<Episode> GetEpisodes(int seriesId);
List<Episode> GetEpisodes(int seriesId, int seasonNumber); List<Episode> GetEpisodes(int seriesId, int seasonNumber);
List<Episode> GetEpisodesBySeriesIds(List<int> seriesIds);
List<Episode> GetEpisodesBySceneSeason(int seriesId, int sceneSeasonNumber); List<Episode> GetEpisodesBySceneSeason(int seriesId, int sceneSeasonNumber);
List<Episode> GetEpisodeByFileId(int fileId); List<Episode> GetEpisodeByFileId(int fileId);
List<Episode> EpisodesWithFiles(int seriesId); List<Episode> EpisodesWithFiles(int seriesId);
@ -77,6 +78,11 @@ namespace NzbDrone.Core.Tv
return Query(s => s.SeriesId == seriesId && s.SeasonNumber == seasonNumber).ToList(); return Query(s => s.SeriesId == seriesId && s.SeasonNumber == seasonNumber).ToList();
} }
public List<Episode> GetEpisodesBySeriesIds(List<int> seriesIds)
{
return Query(s => seriesIds.Contains(s.SeriesId)).ToList();
}
public List<Episode> GetEpisodesBySceneSeason(int seriesId, int seasonNumber) public List<Episode> GetEpisodesBySceneSeason(int seriesId, int seasonNumber)
{ {
return Query(s => s.SeriesId == seriesId && s.SceneSeasonNumber == seasonNumber).ToList(); return Query(s => s.SeriesId == seriesId && s.SceneSeasonNumber == seasonNumber).ToList();

View File

@ -211,7 +211,7 @@ namespace NzbDrone.Core.Tv
public void HandleAsync(SeriesDeletedEvent message) 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); _episodeRepository.DeleteMany(episodes);
} }

View File

@ -1,14 +1,15 @@
using NzbDrone.Common.Messaging; using System.Collections.Generic;
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Tv.Events namespace NzbDrone.Core.Tv.Events
{ {
public class SeriesDeletedEvent : IEvent public class SeriesDeletedEvent : IEvent
{ {
public Series Series { get; private set; } public List<Series> Series { get; private set; }
public bool DeleteFiles { get; private set; } public bool DeleteFiles { get; private set; }
public bool AddImportListExclusion { get; private set; } public bool AddImportListExclusion { get; private set; }
public SeriesDeletedEvent(Series series, bool deleteFiles, bool addImportListExclusion) public SeriesDeletedEvent(List<Series> series, bool deleteFiles, bool addImportListExclusion)
{ {
Series = series; Series = series;
DeleteFiles = deleteFiles; DeleteFiles = deleteFiles;

View File

@ -22,7 +22,7 @@ namespace NzbDrone.Core.Tv
Series FindByTitle(string title, int year); Series FindByTitle(string title, int year);
Series FindByTitleInexact(string title); Series FindByTitleInexact(string title);
Series FindByPath(string path); Series FindByPath(string path);
void DeleteSeries(int seriesId, bool deleteFiles, bool addImportListExclusion); void DeleteSeries(List<int> seriesIds, bool deleteFiles, bool addImportListExclusion);
List<Series> GetAllSeries(); List<Series> GetAllSeries();
List<int> AllSeriesTvdbIds(); List<int> AllSeriesTvdbIds();
Dictionary<int, string> GetAllSeriesPaths(); Dictionary<int, string> GetAllSeriesPaths();
@ -149,10 +149,10 @@ namespace NzbDrone.Core.Tv
return _seriesRepository.FindByTitle(title.CleanSeriesTitle(), year); return _seriesRepository.FindByTitle(title.CleanSeriesTitle(), year);
} }
public void DeleteSeries(int seriesId, bool deleteFiles, bool addImportListExclusion) public void DeleteSeries(List<int> seriesIds, bool deleteFiles, bool addImportListExclusion)
{ {
var series = _seriesRepository.Get(seriesId); var series = _seriesRepository.Get(seriesIds).ToList();
_seriesRepository.Delete(seriesId); _seriesRepository.DeleteMany(seriesIds);
_eventAggregator.PublishEvent(new SeriesDeletedEvent(series, deleteFiles, addImportListExclusion)); _eventAggregator.PublishEvent(new SeriesDeletedEvent(series, deleteFiles, addImportListExclusion));
} }

View File

@ -172,7 +172,7 @@ namespace Sonarr.Api.V3.Series
var deleteFiles = Request.GetBooleanQueryParameter("deleteFiles"); var deleteFiles = Request.GetBooleanQueryParameter("deleteFiles");
var addImportListExclusion = Request.GetBooleanQueryParameter("addImportListExclusion"); var addImportListExclusion = Request.GetBooleanQueryParameter("addImportListExclusion");
_seriesService.DeleteSeries(id, deleteFiles, addImportListExclusion); _seriesService.DeleteSeries(new List<int> { id }, deleteFiles, addImportListExclusion);
} }
private SeriesResource GetSeriesResource(NzbDrone.Core.Tv.Series series, bool includeSeasonImages) private SeriesResource GetSeriesResource(NzbDrone.Core.Tv.Series series, bool includeSeasonImages)
@ -292,7 +292,10 @@ namespace Sonarr.Api.V3.Series
[NonAction] [NonAction]
public void Handle(SeriesDeletedEvent message) public void Handle(SeriesDeletedEvent message)
{ {
BroadcastResourceChange(ModelAction.Deleted, message.Series.ToResource()); foreach (var series in message.Series)
{
BroadcastResourceChange(ModelAction.Deleted, series.ToResource());
}
} }
[NonAction] [NonAction]

View File

@ -94,10 +94,7 @@ namespace Sonarr.Api.V3.Series
[HttpDelete] [HttpDelete]
public object DeleteSeries([FromBody] SeriesEditorResource resource) public object DeleteSeries([FromBody] SeriesEditorResource resource)
{ {
foreach (var seriesId in resource.SeriesIds) _seriesService.DeleteSeries(resource.SeriesIds, resource.DeleteFiles, resource.AddImportListExclusion);
{
_seriesService.DeleteSeries(seriesId, resource.DeleteFiles, resource.AddImportListExclusion);
}
return new { }; return new { };
} }