Fixed: Speed up mass deletes from Movie Editor (#4463)

pull/4012/head
Qstick 5 years ago committed by GitHub
parent 913037c45e
commit 7adb358d1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -20,9 +20,9 @@ namespace NzbDrone.Api.Movies
public class MovieModule : RadarrRestModuleWithSignalR<MovieResource, Movie>, public class MovieModule : RadarrRestModuleWithSignalR<MovieResource, Movie>,
IHandle<MovieImportedEvent>, IHandle<MovieImportedEvent>,
IHandle<MovieFileDeletedEvent>, IHandle<MovieFileDeletedEvent>,
IHandle<MoviesDeletedEvent>,
IHandle<MovieUpdatedEvent>, IHandle<MovieUpdatedEvent>,
IHandle<MovieEditedEvent>, IHandle<MovieEditedEvent>,
IHandle<MovieDeletedEvent>,
IHandle<MovieRenamedEvent>, IHandle<MovieRenamedEvent>,
IHandle<MediaCoversUpdatedEvent> IHandle<MediaCoversUpdatedEvent>
{ {
@ -168,19 +168,22 @@ namespace NzbDrone.Api.Movies
BroadcastResourceChange(ModelAction.Updated, message.MovieFile.MovieId); BroadcastResourceChange(ModelAction.Updated, message.MovieFile.MovieId);
} }
public void Handle(MovieUpdatedEvent message) public void Handle(MoviesDeletedEvent message)
{ {
BroadcastResourceChange(ModelAction.Updated, message.Movie.Id); foreach (var movie in message.Movies)
{
BroadcastResourceChange(ModelAction.Deleted, movie.Id);
}
} }
public void Handle(MovieEditedEvent message) public void Handle(MovieUpdatedEvent message)
{ {
BroadcastResourceChange(ModelAction.Updated, message.Movie.Id); BroadcastResourceChange(ModelAction.Updated, message.Movie.Id);
} }
public void Handle(MovieDeletedEvent message) public void Handle(MovieEditedEvent message)
{ {
BroadcastResourceChange(ModelAction.Deleted, message.Movie.ToResource()); BroadcastResourceChange(ModelAction.Updated, message.Movie.Id);
} }
public void Handle(MovieRenamedEvent message) public void Handle(MovieRenamedEvent message)

@ -1,10 +1,12 @@
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.Blacklisting; using NzbDrone.Core.Blacklisting;
using NzbDrone.Core.Languages; using NzbDrone.Core.Languages;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -14,6 +16,8 @@ namespace NzbDrone.Core.Test.Blacklisting
public class BlacklistRepositoryFixture : DbTest<BlacklistRepository, Blacklist> public class BlacklistRepositoryFixture : DbTest<BlacklistRepository, Blacklist>
{ {
private Blacklist _blacklist; private Blacklist _blacklist;
private Movie _movie1;
private Movie _movie2;
[SetUp] [SetUp]
public void Setup() public void Setup()
@ -26,6 +30,14 @@ namespace NzbDrone.Core.Test.Blacklisting
SourceTitle = "movie.title.1998", SourceTitle = "movie.title.1998",
Date = DateTime.UtcNow Date = DateTime.UtcNow
}; };
_movie1 = Builder<Movie>.CreateNew()
.With(s => s.Id = 7)
.Build();
_movie2 = Builder<Movie>.CreateNew()
.With(s => s.Id = 8)
.Build();
} }
[Test] [Test]
@ -50,5 +62,30 @@ namespace NzbDrone.Core.Test.Blacklisting
Subject.BlacklistedByTitle(_blacklist.MovieId, _blacklist.SourceTitle.ToUpperInvariant()).Should().HaveCount(1); Subject.BlacklistedByTitle(_blacklist.MovieId, _blacklist.SourceTitle.ToUpperInvariant()).Should().HaveCount(1);
} }
[Test]
public void should_delete_blacklists_by_movieId()
{
var blacklistItems = Builder<Blacklist>.CreateListOfSize(5)
.TheFirst(1)
.With(c => c.MovieId = _movie2.Id)
.TheRest()
.With(c => c.MovieId = _movie1.Id)
.All()
.With(c => c.Quality = new QualityModel())
.With(c => c.Languages = new List<Language>())
.With(c => c.Id = 0)
.BuildListOfNew();
Db.InsertMany(blacklistItems);
Subject.DeleteForMovies(new List<int> { _movie1.Id });
var removedMovieBlacklists = Subject.BlacklistedByMovies(new List<int> { _movie1.Id });
var nonRemovedMovieBlacklists = Subject.BlacklistedByMovies(new List<int> { _movie2.Id });
removedMovieBlacklists.Should().HaveCount(0);
nonRemovedMovieBlacklists.Should().HaveCount(1);
}
} }
} }

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Download.History;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Download.DownloadHistoryTests
{
[TestFixture]
public class DownloadHistoryRepositoryFixture : DbTest<DownloadHistoryRepository, DownloadHistory>
{
private Movie _movie1;
private Movie _movie2;
[SetUp]
public void Setup()
{
_movie1 = Builder<Movie>.CreateNew()
.With(s => s.Id = 7)
.Build();
_movie2 = Builder<Movie>.CreateNew()
.With(s => s.Id = 8)
.Build();
}
[Test]
public void should_delete_history_items_by_movieId()
{
var items = Builder<DownloadHistory>.CreateListOfSize(5)
.TheFirst(1)
.With(c => c.Id = 0)
.With(c => c.MovieId = _movie2.Id)
.TheRest()
.With(c => c.Id = 0)
.With(c => c.MovieId = _movie1.Id)
.BuildListOfNew();
Db.InsertMany(items);
Subject.DeleteByMovieIds(new List<int> { _movie1.Id });
var removedItems = Subject.All().Where(h => h.MovieId == _movie1.Id);
var nonRemovedItems = Subject.All().Where(h => h.MovieId == _movie2.Id);
removedItems.Should().HaveCount(0);
nonRemovedItems.Should().HaveCount(1);
}
}
}

@ -1,3 +1,4 @@
using System.Collections.Generic;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
@ -38,7 +39,7 @@ namespace NzbDrone.Core.Test.MediaFiles
Db.InsertMany(files); Db.InsertMany(files);
Subject.DeleteByMovieId(_movie1.Id); Subject.DeleteByMovieIds(new List<int> { _movie1.Id });
var remainingFiles = Subject.AllByMovieId(_movie1.Id); var remainingFiles = Subject.AllByMovieId(_movie1.Id);

@ -1,3 +1,4 @@
using System.Collections.Generic;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
@ -47,7 +48,7 @@ namespace NzbDrone.Core.Test.MediaFiles
Db.InsertMany(files); Db.InsertMany(files);
Subject.DeleteForMovie(_movie.Id); Subject.DeleteForMovies(new List<int> { _movie.Id });
var remainingFiles = Subject.GetFilesByMovie(_movie.Id); var remainingFiles = Subject.GetFilesByMovie(_movie.Id);

@ -6,6 +6,7 @@ using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.History; using NzbDrone.Core.History;
using NzbDrone.Core.Languages; using NzbDrone.Core.Languages;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -14,9 +15,19 @@ namespace NzbDrone.Core.Test.HistoryTests
[TestFixture] [TestFixture]
public class HistoryRepositoryFixture : DbTest<HistoryRepository, MovieHistory> public class HistoryRepositoryFixture : DbTest<HistoryRepository, MovieHistory>
{ {
private Movie _movie1;
private Movie _movie2;
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
_movie1 = Builder<Movie>.CreateNew()
.With(s => s.Id = 7)
.Build();
_movie2 = Builder<Movie>.CreateNew()
.With(s => s.Id = 8)
.Build();
} }
[Test] [Test]
@ -112,5 +123,31 @@ namespace NzbDrone.Core.Test.HistoryTests
movieHistory.Should().HaveCount(2); movieHistory.Should().HaveCount(2);
movieHistory.First().EventType.Should().Be(MovieHistoryEventType.Grabbed); movieHistory.First().EventType.Should().Be(MovieHistoryEventType.Grabbed);
} }
[Test]
public void should_delete_history_items_by_movieId()
{
var items = Builder<MovieHistory>.CreateListOfSize(5)
.TheFirst(1)
.With(c => c.MovieId = _movie2.Id)
.TheRest()
.With(c => c.MovieId = _movie1.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 = MovieHistoryEventType.Grabbed)
.BuildListOfNew();
Db.InsertMany(items);
Subject.DeleteForMovies(new List<int> { _movie1.Id });
var removedItems = Subject.GetByMovieId(_movie1.Id, null);
var nonRemovedItems = Subject.GetByMovieId(_movie2.Id, null);
removedItems.Should().HaveCount(0);
nonRemovedItems.Should().HaveCount(1);
}
} }
} }

@ -4,6 +4,7 @@ using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Languages; using NzbDrone.Core.Languages;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -12,9 +13,19 @@ namespace NzbDrone.Core.Test.MediaFiles
[TestFixture] [TestFixture]
public class MediaFileRepositoryFixture : DbTest<MediaFileRepository, MovieFile> public class MediaFileRepositoryFixture : DbTest<MediaFileRepository, MovieFile>
{ {
private Movie _movie1;
private Movie _movie2;
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
_movie1 = Builder<Movie>.CreateNew()
.With(s => s.Id = 7)
.Build();
_movie2 = Builder<Movie>.CreateNew()
.With(s => s.Id = 8)
.Build();
} }
[Test] [Test]
@ -36,5 +47,30 @@ namespace NzbDrone.Core.Test.MediaFiles
movieFiles.Should().HaveCount(4); movieFiles.Should().HaveCount(4);
movieFiles.Should().OnlyContain(c => c.MovieId == 12); movieFiles.Should().OnlyContain(c => c.MovieId == 12);
} }
[Test]
public void should_delete_files_by_movieId()
{
var items = Builder<MovieFile>.CreateListOfSize(5)
.TheFirst(1)
.With(c => c.MovieId = _movie2.Id)
.TheRest()
.With(c => c.MovieId = _movie1.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.DeleteForMovies(new List<int> { _movie1.Id });
var removedItems = Subject.GetFilesByMovie(_movie1.Id);
var nonRemovedItems = Subject.GetFilesByMovie(_movie2.Id);
removedItems.Should().HaveCount(0);
nonRemovedItems.Should().HaveCount(1);
}
} }
} }

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Movies.Credits;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.MovieTests.CreditTests
{
[TestFixture]
public class CreditRepositoryFixture : DbTest<CreditRepository, Credit>
{
private Movie _movie1;
private Movie _movie2;
[SetUp]
public void Setup()
{
_movie1 = Builder<Movie>.CreateNew()
.With(s => s.Id = 7)
.Build();
_movie2 = Builder<Movie>.CreateNew()
.With(s => s.Id = 8)
.Build();
}
[Test]
public void should_delete_credits_by_movieId()
{
var credits = Builder<Credit>.CreateListOfSize(5)
.TheFirst(1)
.With(c => c.Id = 0)
.With(c => c.MovieId = _movie2.Id)
.TheRest()
.With(c => c.Id = 0)
.With(c => c.MovieId = _movie1.Id)
.BuildListOfNew();
Db.InsertMany(credits);
Subject.DeleteForMovies(new List<int> { _movie1.Id });
var removedMovieCredits = Subject.FindByMovieId(_movie1.Id);
var nonRemovedMovieCredits = Subject.FindByMovieId(_movie2.Id);
removedMovieCredits.Should().HaveCount(0);
nonRemovedMovieCredits.Should().HaveCount(1);
}
}
}

@ -9,7 +9,8 @@ namespace NzbDrone.Core.Blacklisting
{ {
List<Blacklist> BlacklistedByTitle(int movieId, string sourceTitle); List<Blacklist> BlacklistedByTitle(int movieId, string sourceTitle);
List<Blacklist> BlacklistedByTorrentInfoHash(int movieId, string torrentInfoHash); List<Blacklist> BlacklistedByTorrentInfoHash(int movieId, string torrentInfoHash);
List<Blacklist> BlacklistedByMovie(int movieId); List<Blacklist> BlacklistedByMovies(List<int> movieIds);
void DeleteForMovies(List<int> movieIds);
} }
public class BlacklistRepository : BasicRepository<Blacklist>, IBlacklistRepository public class BlacklistRepository : BasicRepository<Blacklist>, IBlacklistRepository
@ -29,9 +30,14 @@ namespace NzbDrone.Core.Blacklisting
return Query(x => x.MovieId == movieId && x.TorrentInfoHash.Contains(torrentInfoHash)); return Query(x => x.MovieId == movieId && x.TorrentInfoHash.Contains(torrentInfoHash));
} }
public List<Blacklist> BlacklistedByMovie(int movieId) public List<Blacklist> BlacklistedByMovies(List<int> movieIds)
{ {
return Query(x => x.MovieId == movieId); return Query(x => movieIds.Contains(x.MovieId));
}
public void DeleteForMovies(List<int> movieIds)
{
Delete(x => movieIds.Contains(x.MovieId));
} }
protected override SqlBuilder PagedBuilder() => new SqlBuilder().Join<Blacklist, Movie>((b, m) => b.MovieId == m.Id); protected override SqlBuilder PagedBuilder() => new SqlBuilder().Join<Blacklist, Movie>((b, m) => b.MovieId == m.Id);

@ -23,7 +23,7 @@ namespace NzbDrone.Core.Blacklisting
IExecute<ClearBlacklistCommand>, IExecute<ClearBlacklistCommand>,
IHandle<DownloadFailedEvent>, IHandle<DownloadFailedEvent>,
IHandleAsync<MovieDeletedEvent> IHandleAsync<MoviesDeletedEvent>
{ {
private readonly IBlacklistRepository _blacklistRepository; private readonly IBlacklistRepository _blacklistRepository;
@ -160,11 +160,9 @@ namespace NzbDrone.Core.Blacklisting
_blacklistRepository.Insert(blacklist); _blacklistRepository.Insert(blacklist);
} }
public void HandleAsync(MovieDeletedEvent message) public void HandleAsync(MoviesDeletedEvent message)
{ {
var blacklisted = _blacklistRepository.BlacklistedByMovie(message.Movie.Id); _blacklistRepository.DeleteForMovies(message.Movies.Select(m => m.Id).ToList());
_blacklistRepository.DeleteMany(blacklisted);
} }
} }
} }

@ -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 DeleteByMovieId(int movieId); void DeleteByMovieIds(List<int> movieIds);
} }
public class DownloadHistoryRepository : BasicRepository<DownloadHistory>, IDownloadHistoryRepository public class DownloadHistoryRepository : BasicRepository<DownloadHistory>, IDownloadHistoryRepository
@ -23,9 +23,9 @@ namespace NzbDrone.Core.Download.History
return Query(x => x.DownloadId == downloadId).OrderByDescending(h => h.Date).ToList(); return Query(x => x.DownloadId == downloadId).OrderByDescending(h => h.Date).ToList();
} }
public void DeleteByMovieId(int movieId) public void DeleteByMovieIds(List<int> movieIds)
{ {
Delete(r => r.MovieId == movieId); Delete(r => movieIds.Contains(r.MovieId));
} }
} }
} }

@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.History; using NzbDrone.Core.History;
using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.MediaFiles.Events;
@ -19,7 +20,7 @@ namespace NzbDrone.Core.Download.History
IHandle<DownloadCompletedEvent>, IHandle<DownloadCompletedEvent>,
IHandle<DownloadFailedEvent>, IHandle<DownloadFailedEvent>,
IHandle<DownloadIgnoredEvent>, IHandle<DownloadIgnoredEvent>,
IHandle<MovieDeletedEvent> IHandle<MoviesDeletedEvent>
{ {
private readonly IDownloadHistoryRepository _repository; private readonly IDownloadHistoryRepository _repository;
private readonly IHistoryService _historyService; private readonly IHistoryService _historyService;
@ -213,9 +214,9 @@ namespace NzbDrone.Core.Download.History
_repository.Insert(history); _repository.Insert(history);
} }
public void Handle(MovieDeletedEvent message) public void Handle(MoviesDeletedEvent message)
{ {
_repository.DeleteByMovieId(message.Movie.Id); _repository.DeleteByMovieIds(message.Movies.Select(m => m.Id).ToList());
} }
} }
} }

@ -6,7 +6,7 @@ namespace NzbDrone.Core.Download.Pending
{ {
public interface IPendingReleaseRepository : IBasicRepository<PendingRelease> public interface IPendingReleaseRepository : IBasicRepository<PendingRelease>
{ {
void DeleteByMovieId(int movieId); void DeleteByMovieIds(List<int> movieIds);
List<PendingRelease> AllByMovieId(int movieId); List<PendingRelease> AllByMovieId(int movieId);
List<PendingRelease> WithoutFallback(); List<PendingRelease> WithoutFallback();
} }
@ -18,9 +18,9 @@ namespace NzbDrone.Core.Download.Pending
{ {
} }
public void DeleteByMovieId(int movieId) public void DeleteByMovieIds(List<int> movieIds)
{ {
Delete(x => x.MovieId == movieId); Delete(x => movieIds.Contains(x.MovieId));
} }
public List<PendingRelease> AllByMovieId(int movieId) public List<PendingRelease> AllByMovieId(int movieId)

@ -34,7 +34,7 @@ namespace NzbDrone.Core.Download.Pending
public class PendingReleaseService : IPendingReleaseService, public class PendingReleaseService : IPendingReleaseService,
IHandle<MovieGrabbedEvent>, IHandle<MovieGrabbedEvent>,
IHandle<MovieDeletedEvent>, IHandle<MoviesDeletedEvent>,
IHandle<RssSyncCompleteEvent> IHandle<RssSyncCompleteEvent>
{ {
private readonly IIndexerStatusService _indexerStatusService; private readonly IIndexerStatusService _indexerStatusService;
@ -408,9 +408,9 @@ namespace NzbDrone.Core.Download.Pending
return 1; return 1;
} }
public void Handle(MovieDeletedEvent message) public void Handle(MoviesDeletedEvent message)
{ {
_repository.DeleteByMovieId(message.Movie.Id); _repository.DeleteByMovieIds(message.Movies.Select(m => m.Id).ToList());
} }
public void Handle(MovieGrabbedEvent message) public void Handle(MovieGrabbedEvent message)

@ -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 DeleteForMovie(int movieId); void DeleteForMovies(List<int> movieIds);
void DeleteForMovieFile(int movieFileId); void DeleteForMovieFile(int movieFileId);
List<TExtraFile> GetFilesByMovie(int movieId); List<TExtraFile> GetFilesByMovie(int movieId);
List<TExtraFile> GetFilesByMovieFile(int movieFileId); List<TExtraFile> GetFilesByMovieFile(int movieFileId);
@ -23,9 +23,9 @@ namespace NzbDrone.Core.Extras.Files
{ {
} }
public void DeleteForMovie(int movieId) public void DeleteForMovies(List<int> movieIds)
{ {
Delete(x => x.MovieId == movieId); Delete(x => movieIds.Contains(x.MovieId));
} }
public void DeleteForMovieFile(int movieFileId) public void DeleteForMovieFile(int movieFileId)

@ -25,8 +25,8 @@ namespace NzbDrone.Core.Extras.Files
} }
public abstract class ExtraFileService<TExtraFile> : IExtraFileService<TExtraFile>, public abstract class ExtraFileService<TExtraFile> : IExtraFileService<TExtraFile>,
IHandleAsync<MovieDeletedEvent>, IHandleAsync<MovieFileDeletedEvent>,
IHandleAsync<MovieFileDeletedEvent> IHandleAsync<MoviesDeletedEvent>
where TExtraFile : ExtraFile, new() where TExtraFile : ExtraFile, new()
{ {
private readonly IExtraFileRepository<TExtraFile> _repository; private readonly IExtraFileRepository<TExtraFile> _repository;
@ -94,10 +94,9 @@ namespace NzbDrone.Core.Extras.Files
_repository.DeleteMany(ids); _repository.DeleteMany(ids);
} }
public void HandleAsync(MovieDeletedEvent message) public void HandleAsync(MoviesDeletedEvent message)
{ {
_logger.Debug("Deleting Extra from database for movie: {0}", message.Movie); _repository.DeleteForMovies(message.Movies.Select(m => m.Id).ToList());
_repository.DeleteForMovie(message.Movie.Id);
} }
public void HandleAsync(MovieFileDeletedEvent message) public void HandleAsync(MovieFileDeletedEvent message)

@ -6,8 +6,8 @@ using NzbDrone.Core.Movies.Events;
namespace NzbDrone.Core.HealthCheck.Checks namespace NzbDrone.Core.HealthCheck.Checks
{ {
[CheckOn(typeof(MovieUpdatedEvent))] [CheckOn(typeof(MovieUpdatedEvent))]
[CheckOn(typeof(MovieDeletedEvent), CheckOnCondition.FailedOnly)] [CheckOn(typeof(MoviesDeletedEvent), CheckOnCondition.FailedOnly)]
public class RemovedSeriesCheck : HealthCheckBase, ICheckOnCondition<MovieUpdatedEvent>, ICheckOnCondition<MovieDeletedEvent> public class RemovedSeriesCheck : HealthCheckBase, ICheckOnCondition<MovieUpdatedEvent>, ICheckOnCondition<MoviesDeletedEvent>
{ {
private readonly IMovieService _movieService; private readonly IMovieService _movieService;
@ -35,9 +35,9 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format("Movie {0} was removed from TMDb", movieText), "#movie-was-removed-from-tmdb"); return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format("Movie {0} was removed from TMDb", movieText), "#movie-was-removed-from-tmdb");
} }
public bool ShouldCheckOnEvent(MovieDeletedEvent message) public bool ShouldCheckOnEvent(MoviesDeletedEvent message)
{ {
return message.Movie.Status == MovieStatusType.Deleted; return message.Movies.Any(m => m.Status == MovieStatusType.Deleted);
} }
public bool ShouldCheckOnEvent(MovieUpdatedEvent message) public bool ShouldCheckOnEvent(MovieUpdatedEvent message)

@ -7,7 +7,7 @@ using NzbDrone.Core.RootFolders;
namespace NzbDrone.Core.HealthCheck.Checks namespace NzbDrone.Core.HealthCheck.Checks
{ {
[CheckOn(typeof(MovieDeletedEvent))] [CheckOn(typeof(MoviesDeletedEvent))]
[CheckOn(typeof(MovieMovedEvent))] [CheckOn(typeof(MovieMovedEvent))]
[CheckOn(typeof(MoviesImportedEvent), CheckOnCondition.FailedOnly)] [CheckOn(typeof(MoviesImportedEvent), CheckOnCondition.FailedOnly)]
[CheckOn(typeof(MovieImportFailedEvent), CheckOnCondition.SuccessfulOnly)] [CheckOn(typeof(MovieImportFailedEvent), CheckOnCondition.SuccessfulOnly)]

@ -16,7 +16,7 @@ namespace NzbDrone.Core.History
List<MovieHistory> FindByDownloadId(string downloadId); List<MovieHistory> FindByDownloadId(string downloadId);
List<MovieHistory> FindDownloadHistory(int movieId, QualityModel quality); List<MovieHistory> FindDownloadHistory(int movieId, QualityModel quality);
List<MovieHistory> GetByMovieId(int movieId, MovieHistoryEventType? eventType); List<MovieHistory> GetByMovieId(int movieId, MovieHistoryEventType? eventType);
void DeleteForMovie(int movieId); void DeleteForMovies(List<int> movieIds);
MovieHistory MostRecentForMovie(int movieId); MovieHistory MostRecentForMovie(int movieId);
List<MovieHistory> Since(DateTime date, MovieHistoryEventType? eventType); List<MovieHistory> Since(DateTime date, MovieHistoryEventType? eventType);
} }
@ -68,9 +68,9 @@ namespace NzbDrone.Core.History
return query.OrderByDescending(h => h.Date).ToList(); return query.OrderByDescending(h => h.Date).ToList();
} }
public void DeleteForMovie(int movieId) public void DeleteForMovies(List<int> movieIds)
{ {
Delete(c => c.MovieId == movieId); Delete(c => movieIds.Contains(c.MovieId));
} }
protected override SqlBuilder PagedBuilder() => new SqlBuilder() protected override SqlBuilder PagedBuilder() => new SqlBuilder()

@ -37,7 +37,7 @@ namespace NzbDrone.Core.History
IHandle<DownloadFailedEvent>, IHandle<DownloadFailedEvent>,
IHandle<MovieFileDeletedEvent>, IHandle<MovieFileDeletedEvent>,
IHandle<MovieFileRenamedEvent>, IHandle<MovieFileRenamedEvent>,
IHandle<MovieDeletedEvent>, IHandle<MoviesDeletedEvent>,
IHandle<DownloadIgnoredEvent> IHandle<DownloadIgnoredEvent>
{ {
private readonly IHistoryRepository _historyRepository; private readonly IHistoryRepository _historyRepository;
@ -247,9 +247,9 @@ namespace NzbDrone.Core.History
_historyRepository.Insert(history); _historyRepository.Insert(history);
} }
public void Handle(MovieDeletedEvent message) public void Handle(MoviesDeletedEvent message)
{ {
_historyRepository.DeleteForMovie(message.Movie.Id); _historyRepository.DeleteForMovies(message.Movies.Select(m => m.Id).ToList());
} }
public string FindDownloadId(MovieImportedEvent trackedDownload) public string FindDownloadId(MovieImportedEvent trackedDownload)

@ -23,7 +23,7 @@ namespace NzbDrone.Core.MediaCover
public class MediaCoverService : public class MediaCoverService :
IHandleAsync<MovieUpdatedEvent>, IHandleAsync<MovieUpdatedEvent>,
IHandleAsync<MovieDeletedEvent>, IHandleAsync<MoviesDeletedEvent>,
IMapCoversToLocal IMapCoversToLocal
{ {
private readonly IImageResizer _resizer; private readonly IImageResizer _resizer;
@ -195,12 +195,15 @@ namespace NzbDrone.Core.MediaCover
_eventAggregator.PublishEvent(new MediaCoversUpdatedEvent(message.Movie, updated)); _eventAggregator.PublishEvent(new MediaCoversUpdatedEvent(message.Movie, updated));
} }
public void HandleAsync(MovieDeletedEvent message) public void HandleAsync(MoviesDeletedEvent message)
{ {
var path = GetMovieCoverPath(message.Movie.Id); foreach (var movie in message.Movies)
if (_diskProvider.FolderExists(path))
{ {
_diskProvider.DeleteFolder(path, true); var path = GetMovieCoverPath(movie.Id);
if (_diskProvider.FolderExists(path))
{
_diskProvider.DeleteFolder(path, true);
}
} }
} }
} }

@ -20,7 +20,7 @@ namespace NzbDrone.Core.MediaFiles
} }
public class MediaFileDeletionService : IDeleteMediaFiles, public class MediaFileDeletionService : IDeleteMediaFiles,
IHandleAsync<MovieDeletedEvent>, IHandleAsync<MoviesDeletedEvent>,
IHandle<MovieFileDeletedEvent> IHandle<MovieFileDeletedEvent>
{ {
private readonly IDiskProvider _diskProvider; private readonly IDiskProvider _diskProvider;
@ -83,36 +83,38 @@ namespace NzbDrone.Core.MediaFiles
_mediaFileService.Delete(movieFile, DeleteMediaFileReason.Manual); _mediaFileService.Delete(movieFile, DeleteMediaFileReason.Manual);
} }
public void HandleAsync(MovieDeletedEvent message) public void HandleAsync(MoviesDeletedEvent message)
{ {
if (message.DeleteFiles) if (message.DeleteFiles)
{ {
var movie = message.Movie;
var allMovies = _movieService.GetAllMovies(); var allMovies = _movieService.GetAllMovies();
foreach (var s in allMovies) foreach (var movie in message.Movies)
{ {
if (s.Id == movie.Id) foreach (var s in allMovies)
{ {
continue; if (s.Id == movie.Id)
{
continue;
}
if (movie.Path.IsParentPath(s.Path))
{
_logger.Error("Movie path: '{0}' is a parent of another movie, not deleting files.", movie.Path);
return;
}
if (movie.Path.PathEquals(s.Path))
{
_logger.Error("Movie path: '{0}' is the same as another movie, not deleting files.", movie.Path);
return;
}
} }
if (movie.Path.IsParentPath(s.Path)) if (_diskProvider.FolderExists(movie.Path))
{ {
_logger.Error("Movie path: '{0}' is a parent of another movie, not deleting files.", movie.Path); _recycleBinProvider.DeleteFolder(movie.Path);
return;
} }
if (movie.Path.PathEquals(s.Path))
{
_logger.Error("Movie path: '{0}' is the same as another movie, not deleting files.", movie.Path);
return;
}
}
if (_diskProvider.FolderExists(message.Movie.Path))
{
_recycleBinProvider.DeleteFolder(message.Movie.Path);
} }
} }
} }

@ -8,6 +8,7 @@ namespace NzbDrone.Core.MediaFiles
{ {
List<MovieFile> GetFilesByMovie(int movieId); List<MovieFile> GetFilesByMovie(int movieId);
List<MovieFile> GetFilesWithoutMediaInfo(); List<MovieFile> GetFilesWithoutMediaInfo();
void DeleteForMovies(List<int> movieIds);
} }
public class MediaFileRepository : BasicRepository<MovieFile>, IMediaFileRepository public class MediaFileRepository : BasicRepository<MovieFile>, IMediaFileRepository
@ -26,5 +27,10 @@ namespace NzbDrone.Core.MediaFiles
{ {
return Query(x => x.MediaInfo == null); return Query(x => x.MediaInfo == null);
} }
public void DeleteForMovies(List<int> movieIds)
{
Delete(x => movieIds.Contains(x.MovieId));
}
} }
} }

@ -22,7 +22,7 @@ namespace NzbDrone.Core.MediaFiles
List<MovieFile> GetMovies(IEnumerable<int> ids); List<MovieFile> GetMovies(IEnumerable<int> ids);
} }
public class MediaFileService : IMediaFileService, IHandleAsync<MovieDeletedEvent> public class MediaFileService : IMediaFileService, IHandleAsync<MoviesDeletedEvent>
{ {
private readonly IMediaFileRepository _mediaFileRepository; private readonly IMediaFileRepository _mediaFileRepository;
private readonly IMovieRepository _movieRepository; private readonly IMovieRepository _movieRepository;
@ -106,10 +106,9 @@ namespace NzbDrone.Core.MediaFiles
return _mediaFileRepository.Get(id); return _mediaFileRepository.Get(id);
} }
public void HandleAsync(MovieDeletedEvent message) public void HandleAsync(MoviesDeletedEvent message)
{ {
var files = GetFilesByMovie(message.Movie.Id); _mediaFileRepository.DeleteForMovies(message.Movies.Select(m => m.Id).ToList());
_mediaFileRepository.DeleteMany(files);
} }
} }
} }

@ -10,6 +10,7 @@ namespace NzbDrone.Core.Movies.AlternativeTitles
AlternativeTitle FindBySourceId(int sourceId); AlternativeTitle FindBySourceId(int sourceId);
List<AlternativeTitle> FindBySourceIds(List<int> sourceIds); List<AlternativeTitle> FindBySourceIds(List<int> sourceIds);
List<AlternativeTitle> FindByMovieId(int movieId); List<AlternativeTitle> FindByMovieId(int movieId);
void DeleteForMovies(List<int> movieIds);
} }
public class AlternativeTitleRepository : BasicRepository<AlternativeTitle>, IAlternativeTitleRepository public class AlternativeTitleRepository : BasicRepository<AlternativeTitle>, IAlternativeTitleRepository
@ -33,5 +34,10 @@ namespace NzbDrone.Core.Movies.AlternativeTitles
{ {
return Query(x => x.MovieId == movieId); return Query(x => x.MovieId == movieId);
} }
public void DeleteForMovies(List<int> movieIds)
{
Delete(x => movieIds.Contains(x.MovieId));
}
} }
} }

@ -18,7 +18,7 @@ namespace NzbDrone.Core.Movies.AlternativeTitles
List<AlternativeTitle> UpdateTitles(List<AlternativeTitle> titles, Movie movie); List<AlternativeTitle> UpdateTitles(List<AlternativeTitle> titles, Movie movie);
} }
public class AlternativeTitleService : IAlternativeTitleService, IHandleAsync<MovieDeletedEvent> public class AlternativeTitleService : IAlternativeTitleService, IHandleAsync<MoviesDeletedEvent>
{ {
private readonly IAlternativeTitleRepository _titleRepo; private readonly IAlternativeTitleRepository _titleRepo;
private readonly IConfigService _configService; private readonly IConfigService _configService;
@ -99,10 +99,9 @@ namespace NzbDrone.Core.Movies.AlternativeTitles
return titles; return titles;
} }
public void HandleAsync(MovieDeletedEvent message) public void HandleAsync(MoviesDeletedEvent message)
{ {
var title = GetAllTitlesForMovie(message.Movie.Id); _titleRepo.DeleteForMovies(message.Movies.Select(m => m.Id).ToList());
_titleRepo.DeleteMany(title);
} }
} }
} }

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
@ -7,6 +7,7 @@ namespace NzbDrone.Core.Movies.Credits
public interface ICreditRepository : IBasicRepository<Credit> public interface ICreditRepository : IBasicRepository<Credit>
{ {
List<Credit> FindByMovieId(int movieId); List<Credit> FindByMovieId(int movieId);
void DeleteForMovies(List<int> movieIds);
} }
public class CreditRepository : BasicRepository<Credit>, ICreditRepository public class CreditRepository : BasicRepository<Credit>, ICreditRepository
@ -20,5 +21,10 @@ namespace NzbDrone.Core.Movies.Credits
{ {
return Query(x => x.MovieId == movieId); return Query(x => x.MovieId == movieId);
} }
public void DeleteForMovies(List<int> movieIds)
{
Delete(x => movieIds.Contains(x.MovieId));
}
} }
} }

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
@ -16,7 +16,7 @@ namespace NzbDrone.Core.Movies.Credits
List<Credit> UpdateCredits(List<Credit> credits, Movie movie); List<Credit> UpdateCredits(List<Credit> credits, Movie movie);
} }
public class CreditService : ICreditService, IHandleAsync<MovieDeletedEvent> public class CreditService : ICreditService, IHandleAsync<MoviesDeletedEvent>
{ {
private readonly ICreditRepository _creditRepo; private readonly ICreditRepository _creditRepo;
@ -82,9 +82,9 @@ namespace NzbDrone.Core.Movies.Credits
return credits; return credits;
} }
public void HandleAsync(MovieDeletedEvent message) public void HandleAsync(MoviesDeletedEvent message)
{ {
_creditRepo.DeleteMany(GetAllCreditsForMovie(message.Movie.Id)); _creditRepo.DeleteForMovies(message.Movies.Select(m => m.Id).ToList());
} }
} }
} }

@ -1,16 +0,0 @@
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Movies.Events
{
public class MovieDeletedEvent : IEvent
{
public Movie Movie { get; private set; }
public bool DeleteFiles { get; private set; }
public MovieDeletedEvent(Movie movie, bool deleteFiles)
{
Movie = movie;
DeleteFiles = deleteFiles;
}
}
}

@ -0,0 +1,19 @@
using System.Collections.Generic;
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Movies.Events
{
public class MoviesDeletedEvent : IEvent
{
public List<Movie> Movies { get; private set; }
public bool DeleteFiles { get; private set; }
public bool AddExclusion { get; private set; }
public MoviesDeletedEvent(List<Movie> movies, bool deleteFiles, bool addExclusion)
{
Movies = movies;
DeleteFiles = deleteFiles;
AddExclusion = addExclusion;
}
}
}

@ -10,8 +10,6 @@ using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Movies.Events; using NzbDrone.Core.Movies.Events;
using NzbDrone.Core.NetImport.ImportExclusions;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.RomanNumerals; using NzbDrone.Core.Parser.RomanNumerals;
@ -39,6 +37,7 @@ namespace NzbDrone.Core.Movies
PagingSpec<Movie> MoviesWithoutFiles(PagingSpec<Movie> pagingSpec); PagingSpec<Movie> MoviesWithoutFiles(PagingSpec<Movie> pagingSpec);
void SetFileId(Movie movie, MovieFile movieFile); void SetFileId(Movie movie, MovieFile movieFile);
void DeleteMovie(int movieId, bool deleteFiles, bool addExclusion = false); void DeleteMovie(int movieId, bool deleteFiles, bool addExclusion = false);
void DeleteMovies(List<int> movieIds, bool deleteFiles, bool addExclusion = false);
List<Movie> GetAllMovies(); List<Movie> GetAllMovies();
List<Movie> AllForTag(int tagId); List<Movie> AllForTag(int tagId);
Movie UpdateMovie(Movie movie); Movie UpdateMovie(Movie movie);
@ -54,24 +53,18 @@ namespace NzbDrone.Core.Movies
private readonly IMovieRepository _movieRepository; private readonly IMovieRepository _movieRepository;
private readonly IConfigService _configService; private readonly IConfigService _configService;
private readonly IEventAggregator _eventAggregator; private readonly IEventAggregator _eventAggregator;
private readonly IBuildFileNames _fileNameBuilder;
private readonly IImportExclusionsService _exclusionService;
private readonly IBuildMoviePaths _moviePathBuilder; private readonly IBuildMoviePaths _moviePathBuilder;
private readonly Logger _logger; private readonly Logger _logger;
public MovieService(IMovieRepository movieRepository, public MovieService(IMovieRepository movieRepository,
IEventAggregator eventAggregator, IEventAggregator eventAggregator,
IBuildFileNames fileNameBuilder,
IConfigService configService, IConfigService configService,
IImportExclusionsService exclusionService,
IBuildMoviePaths moviePathBuilder, IBuildMoviePaths moviePathBuilder,
Logger logger) Logger logger)
{ {
_movieRepository = movieRepository; _movieRepository = movieRepository;
_eventAggregator = eventAggregator; _eventAggregator = eventAggregator;
_fileNameBuilder = fileNameBuilder;
_configService = configService; _configService = configService;
_exclusionService = exclusionService;
_moviePathBuilder = moviePathBuilder; _moviePathBuilder = moviePathBuilder;
_logger = logger; _logger = logger;
} }
@ -233,14 +226,24 @@ namespace NzbDrone.Core.Movies
public void DeleteMovie(int movieId, bool deleteFiles, bool addExclusion = false) public void DeleteMovie(int movieId, bool deleteFiles, bool addExclusion = false)
{ {
var movie = _movieRepository.Get(movieId); var movie = _movieRepository.Get(movieId);
if (addExclusion)
{
_exclusionService.AddExclusion(new ImportExclusion { TmdbId = movie.TmdbId, MovieTitle = movie.Title, MovieYear = movie.Year });
}
_movieRepository.Delete(movieId); _movieRepository.Delete(movieId);
_eventAggregator.PublishEvent(new MovieDeletedEvent(movie, deleteFiles)); _eventAggregator.PublishEvent(new MoviesDeletedEvent(new List<Movie> { movie }, deleteFiles, addExclusion));
_logger.Info("Deleted movie {}", movie); _logger.Info("Deleted movie {0}", movie);
}
public void DeleteMovies(List<int> movieIds, bool deleteFiles, bool addExclusion = false)
{
var moviesToDelete = _movieRepository.Get(movieIds).ToList();
_movieRepository.DeleteMany(movieIds);
_eventAggregator.PublishEvent(new MoviesDeletedEvent(moviesToDelete, deleteFiles, addExclusion));
foreach (var movie in moviesToDelete)
{
_logger.Info("Deleted movie {0}", movie);
}
} }
public List<Movie> GetAllMovies() public List<Movie> GetAllMovies()

@ -1,35 +1,29 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Movies.Events;
namespace NzbDrone.Core.NetImport.ImportExclusions namespace NzbDrone.Core.NetImport.ImportExclusions
{ {
public interface IImportExclusionsService public interface IImportExclusionsService
{ {
List<ImportExclusion> GetAllExclusions(); List<ImportExclusion> GetAllExclusions();
bool IsMovieExcluded(int tmdbid); bool IsMovieExcluded(int tmdbId);
ImportExclusion AddExclusion(ImportExclusion exclusion); ImportExclusion AddExclusion(ImportExclusion exclusion);
void RemoveExclusion(ImportExclusion exclusion); void RemoveExclusion(ImportExclusion exclusion);
ImportExclusion GetById(int id); ImportExclusion GetById(int id);
} }
public class ImportExclusionsService : IImportExclusionsService public class ImportExclusionsService : IImportExclusionsService, IHandleAsync<MoviesDeletedEvent>
{ {
private readonly IImportExclusionsRepository _exclusionRepository; private readonly IImportExclusionsRepository _exclusionRepository;
private readonly IConfigService _configService;
private readonly IEventAggregator _eventAggregator;
private readonly Logger _logger; private readonly Logger _logger;
public ImportExclusionsService(IImportExclusionsRepository exclusionRepository, public ImportExclusionsService(IImportExclusionsRepository exclusionRepository,
IEventAggregator eventAggregator,
IConfigService configService,
Logger logger) Logger logger)
{ {
_exclusionRepository = exclusionRepository; _exclusionRepository = exclusionRepository;
_eventAggregator = eventAggregator;
_configService = configService;
_logger = logger; _logger = logger;
} }
@ -48,9 +42,9 @@ namespace NzbDrone.Core.NetImport.ImportExclusions
return _exclusionRepository.All().ToList(); return _exclusionRepository.All().ToList();
} }
public bool IsMovieExcluded(int tmdbid) public bool IsMovieExcluded(int tmdbId)
{ {
return _exclusionRepository.IsMovieExcluded(tmdbid); return _exclusionRepository.IsMovieExcluded(tmdbId);
} }
public void RemoveExclusion(ImportExclusion exclusion) public void RemoveExclusion(ImportExclusion exclusion)
@ -62,5 +56,14 @@ namespace NzbDrone.Core.NetImport.ImportExclusions
{ {
return _exclusionRepository.Get(id); return _exclusionRepository.Get(id);
} }
public void HandleAsync(MoviesDeletedEvent message)
{
if (message.AddExclusion)
{
_logger.Debug("Adding {0} Deleted Movies to Net Import Exclusions", message.Movies.Count);
_exclusionRepository.InsertMany(message.Movies.Select(m => new ImportExclusion { TmdbId = m.TmdbId, MovieTitle = m.Title, MovieYear = m.Year }).ToList());
}
}
} }
} }

@ -94,10 +94,7 @@ namespace Radarr.Api.V3.Movies
{ {
var resource = Request.Body.FromJson<MovieEditorResource>(); var resource = Request.Body.FromJson<MovieEditorResource>();
foreach (var id in resource.MovieIds) _movieService.DeleteMovies(resource.MovieIds, resource.DeleteFiles, resource.AddNetImportExclusion);
{
_movieService.DeleteMovie(id, resource.DeleteFiles, resource.AddNetImportExclusion);
}
return new object(); return new object();
} }

@ -25,7 +25,7 @@ namespace Radarr.Api.V3.Movies
IHandle<MovieFileDeletedEvent>, IHandle<MovieFileDeletedEvent>,
IHandle<MovieUpdatedEvent>, IHandle<MovieUpdatedEvent>,
IHandle<MovieEditedEvent>, IHandle<MovieEditedEvent>,
IHandle<MovieDeletedEvent>, IHandle<MoviesDeletedEvent>,
IHandle<MovieRenamedEvent>, IHandle<MovieRenamedEvent>,
IHandle<MediaCoversUpdatedEvent> IHandle<MediaCoversUpdatedEvent>
{ {
@ -220,9 +220,12 @@ namespace Radarr.Api.V3.Movies
BroadcastResourceChange(ModelAction.Updated, message.Movie.Id); BroadcastResourceChange(ModelAction.Updated, message.Movie.Id);
} }
public void Handle(MovieDeletedEvent message) public void Handle(MoviesDeletedEvent message)
{ {
BroadcastResourceChange(ModelAction.Deleted, message.Movie.ToResource()); foreach (var movie in message.Movies)
{
BroadcastResourceChange(ModelAction.Deleted, movie.Id);
}
} }
public void Handle(MovieRenamedEvent message) public void Handle(MovieRenamedEvent message)

Loading…
Cancel
Save