mirror of
https://github.com/Radarr/Radarr.git
synced 2024-09-11 03:52:33 +02:00
New: AddMovieService to validate and populate incomplete adds
This commit is contained in:
parent
8b06df1b1a
commit
93d27c70c4
@ -28,11 +28,13 @@ public class MovieModule : RadarrRestModuleWithSignalR<MovieResource, Movie>,
|
||||
{
|
||||
private const string TITLE_SLUG_ROUTE = "/titleslug/(?<slug>[^/]+)";
|
||||
|
||||
protected readonly IMovieService _moviesService;
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IAddMovieService _addMovieService;
|
||||
private readonly IMapCoversToLocal _coverMapper;
|
||||
|
||||
public MovieModule(IBroadcastSignalRMessage signalRBroadcaster,
|
||||
IMovieService moviesService,
|
||||
IMovieService movieService,
|
||||
IAddMovieService addMovieService,
|
||||
IMapCoversToLocal coverMapper,
|
||||
RootFolderValidator rootFolderValidator,
|
||||
MappedNetworkDriveValidator mappedNetworkDriveValidator,
|
||||
@ -43,7 +45,8 @@ public MovieModule(IBroadcastSignalRMessage signalRBroadcaster,
|
||||
ProfileExistsValidator profileExistsValidator)
|
||||
: base(signalRBroadcaster)
|
||||
{
|
||||
_moviesService = moviesService;
|
||||
_movieService = movieService;
|
||||
_addMovieService = addMovieService;
|
||||
|
||||
_coverMapper = coverMapper;
|
||||
|
||||
@ -80,7 +83,7 @@ public MovieModule(IBroadcastSignalRMessage signalRBroadcaster,
|
||||
|
||||
private MovieResource GetMovie(int id)
|
||||
{
|
||||
var movies = _moviesService.GetMovie(id);
|
||||
var movies = _movieService.GetMovie(id);
|
||||
return MapToResource(movies);
|
||||
}
|
||||
|
||||
@ -99,7 +102,7 @@ protected MovieResource MapToResource(Movie movies)
|
||||
|
||||
private List<MovieResource> AllMovie()
|
||||
{
|
||||
var moviesResources = _moviesService.GetAllMovies().ToResource();
|
||||
var moviesResources = _movieService.GetAllMovies().ToResource();
|
||||
|
||||
MapCoversToLocal(moviesResources.ToArray());
|
||||
|
||||
@ -110,14 +113,14 @@ private int AddMovie(MovieResource moviesResource)
|
||||
{
|
||||
var model = moviesResource.ToModel();
|
||||
|
||||
return _moviesService.AddMovie(model).Id;
|
||||
return _addMovieService.AddMovie(model).Id;
|
||||
}
|
||||
|
||||
private void UpdateMovie(MovieResource moviesResource)
|
||||
{
|
||||
var model = moviesResource.ToModel(_moviesService.GetMovie(moviesResource.Id));
|
||||
var model = moviesResource.ToModel(_movieService.GetMovie(moviesResource.Id));
|
||||
|
||||
_moviesService.UpdateMovie(model);
|
||||
_movieService.UpdateMovie(model);
|
||||
|
||||
BroadcastResourceChange(ModelAction.Updated, moviesResource);
|
||||
}
|
||||
@ -139,7 +142,7 @@ private void DeleteMovie(int id)
|
||||
addExclusion = Convert.ToBoolean(addExclusionQuery.Value);
|
||||
}
|
||||
|
||||
_moviesService.DeleteMovie(id, deleteFiles, addExclusion);
|
||||
_movieService.DeleteMovie(id, deleteFiles, addExclusion);
|
||||
}
|
||||
|
||||
private void MapCoversToLocal(params MovieResource[] movies)
|
||||
|
@ -11,13 +11,13 @@ namespace NzbDrone.Api.NetImport
|
||||
{
|
||||
public class ListImportModule : NzbDroneApiModule
|
||||
{
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IAddMovieService _addMovieService;
|
||||
private readonly ISearchForNewMovie _movieSearch;
|
||||
|
||||
public ListImportModule(IMovieService movieService, ISearchForNewMovie movieSearch)
|
||||
public ListImportModule(IAddMovieService addMovieService, ISearchForNewMovie movieSearch)
|
||||
: base("/movie/import")
|
||||
{
|
||||
_movieService = movieService;
|
||||
_addMovieService = addMovieService;
|
||||
_movieSearch = movieSearch;
|
||||
Put("/", movie => SaveAll());
|
||||
}
|
||||
@ -28,7 +28,7 @@ private object SaveAll()
|
||||
|
||||
var movies = resources.Select(movieResource => _movieSearch.MapMovieToTmdbMovie(movieResource.ToModel())).Where(m => m != null).DistinctBy(m => m.TmdbId).ToList();
|
||||
|
||||
return ResponseWithCode(_movieService.AddMovies(movies).ToResource(), HttpStatusCode.Accepted);
|
||||
return ResponseWithCode(_addMovieService.AddMovies(movies).ToResource(), HttpStatusCode.Accepted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,80 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.BulkImport
|
||||
{
|
||||
[TestFixture]
|
||||
public class AddMultiMoviesFixture : CoreTest<MovieService>
|
||||
{
|
||||
private List<Movie> _fakeMovies;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_fakeMovies = Builder<Movie>.CreateListOfSize(3).BuildList();
|
||||
_fakeMovies.ForEach(m =>
|
||||
{
|
||||
m.Path = null;
|
||||
m.RootFolderPath = @"C:\Test\TV";
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void movies_added_event_should_have_proper_path()
|
||||
{
|
||||
Mocker.GetMock<IBuildFileNames>()
|
||||
.Setup(s => s.GetMovieFolder(It.IsAny<Movie>(), null))
|
||||
.Returns((Movie m, NamingConfig n) => m.Title);
|
||||
|
||||
Mocker.GetMock<IMovieRepository>().Setup(s => s.FindByTmdbId(It.IsAny<List<int>>()))
|
||||
.Returns(new List<Movie>());
|
||||
|
||||
var movies = Subject.AddMovies(_fakeMovies);
|
||||
|
||||
foreach (Movie movie in movies)
|
||||
{
|
||||
movie.Path.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
// Subject.GetAllMovies().Should().HaveCount(3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void movies_added_should_ignore_already_added()
|
||||
{
|
||||
Mocker.GetMock<IBuildFileNames>()
|
||||
.Setup(s => s.GetMovieFolder(It.IsAny<Movie>(), null))
|
||||
.Returns((Movie m, NamingConfig n) => m.Title);
|
||||
|
||||
Mocker.GetMock<IMovieRepository>().Setup(s => s.FindByTmdbId(It.IsAny<List<int>>()))
|
||||
.Returns(new List<Movie> { _fakeMovies[0] });
|
||||
|
||||
var movies = Subject.AddMovies(_fakeMovies);
|
||||
|
||||
Mocker.GetMock<IMovieRepository>().Verify(v => v.InsertMany(It.Is<List<Movie>>(l => l.Count == 2)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void movies_added_should_ignore_duplicates()
|
||||
{
|
||||
Mocker.GetMock<IBuildFileNames>()
|
||||
.Setup(s => s.GetMovieFolder(It.IsAny<Movie>(), null))
|
||||
.Returns((Movie m, NamingConfig n) => m.Title);
|
||||
|
||||
Mocker.GetMock<IMovieRepository>().Setup(s => s.FindByTmdbId(It.IsAny<List<int>>()))
|
||||
.Returns(new List<Movie>());
|
||||
|
||||
_fakeMovies[2].TmdbId = _fakeMovies[0].TmdbId;
|
||||
|
||||
var movies = Subject.AddMovies(_fakeMovies);
|
||||
|
||||
Mocker.GetMock<IMovieRepository>().Verify(v => v.InsertMany(It.Is<List<Movie>>(l => l.Count == 2)));
|
||||
}
|
||||
}
|
||||
}
|
132
src/NzbDrone.Core.Test/MovieTests/AddMovieFixture.cs
Normal file
132
src/NzbDrone.Core.Test/MovieTests/AddMovieFixture.cs
Normal file
@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Movies.Credits;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.MovieTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class AddMovieFixture : CoreTest<AddMovieService>
|
||||
{
|
||||
private Movie _fakeMovie;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_fakeMovie = Builder<Movie>
|
||||
.CreateNew()
|
||||
.With(s => s.Path = null)
|
||||
.Build();
|
||||
}
|
||||
|
||||
private void GivenValidMovie(int tmdbId)
|
||||
{
|
||||
Mocker.GetMock<IProvideMovieInfo>()
|
||||
.Setup(s => s.GetMovieInfo(tmdbId, true))
|
||||
.Returns(new Tuple<Movie, List<Credit>>(_fakeMovie, new List<Credit>()));
|
||||
}
|
||||
|
||||
private void GivenValidPath()
|
||||
{
|
||||
Mocker.GetMock<IBuildFileNames>()
|
||||
.Setup(s => s.GetMovieFolder(It.IsAny<Movie>(), null))
|
||||
.Returns<Movie, NamingConfig>((c, n) => c.Title);
|
||||
|
||||
Mocker.GetMock<IAddMovieValidator>()
|
||||
.Setup(s => s.Validate(It.IsAny<Movie>()))
|
||||
.Returns(new ValidationResult());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_add_a_movie_without_passing_in_title()
|
||||
{
|
||||
var newMovie = new Movie
|
||||
{
|
||||
TmdbId = 1,
|
||||
RootFolderPath = @"C:\Test\Movies"
|
||||
};
|
||||
|
||||
GivenValidMovie(newMovie.TmdbId);
|
||||
GivenValidPath();
|
||||
|
||||
var series = Subject.AddMovie(newMovie);
|
||||
|
||||
series.Title.Should().Be(_fakeMovie.Title);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_have_proper_path()
|
||||
{
|
||||
var newMovie = new Movie
|
||||
{
|
||||
TmdbId = 1,
|
||||
RootFolderPath = @"C:\Test\Movies"
|
||||
};
|
||||
|
||||
GivenValidMovie(newMovie.TmdbId);
|
||||
GivenValidPath();
|
||||
|
||||
var series = Subject.AddMovie(newMovie);
|
||||
|
||||
series.Path.Should().Be(Path.Combine(newMovie.RootFolderPath, _fakeMovie.Title));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_throw_if_movie_validation_fails()
|
||||
{
|
||||
var newMovie = new Movie
|
||||
{
|
||||
TmdbId = 1,
|
||||
Path = @"C:\Test\Movie\Title1"
|
||||
};
|
||||
|
||||
GivenValidMovie(newMovie.TmdbId);
|
||||
|
||||
Mocker.GetMock<IAddMovieValidator>()
|
||||
.Setup(s => s.Validate(It.IsAny<Movie>()))
|
||||
.Returns(new ValidationResult(new List<ValidationFailure>
|
||||
{
|
||||
new ValidationFailure("Path", "Test validation failure")
|
||||
}));
|
||||
|
||||
Assert.Throws<ValidationException>(() => Subject.AddMovie(newMovie));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_throw_if_movie_cannot_be_found()
|
||||
{
|
||||
var newMovie = new Movie
|
||||
{
|
||||
TmdbId = 1,
|
||||
Path = @"C:\Test\Movie\Title1"
|
||||
};
|
||||
|
||||
Mocker.GetMock<IProvideMovieInfo>()
|
||||
.Setup(s => s.GetMovieInfo(newMovie.TmdbId, true))
|
||||
.Throws(new MovieNotFoundException("Movie Not Found"));
|
||||
|
||||
Mocker.GetMock<IAddMovieValidator>()
|
||||
.Setup(s => s.Validate(It.IsAny<Movie>()))
|
||||
.Returns(new ValidationResult(new List<ValidationFailure>
|
||||
{
|
||||
new ValidationFailure("Path", "Test validation failure")
|
||||
}));
|
||||
|
||||
Assert.Throws<ValidationException>(() => Subject.AddMovie(newMovie));
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.MovieTests.MovieServiceTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class AddMovieFixture : CoreTest<MovieService>
|
||||
{
|
||||
private Movie _fakeMovie;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_fakeMovie = Builder<Movie>.CreateNew().Build();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void movie_added_event_should_have_proper_path()
|
||||
{
|
||||
_fakeMovie.Path = null;
|
||||
_fakeMovie.RootFolderPath = @"C:\Test\Movies";
|
||||
|
||||
Mocker.GetMock<IBuildFileNames>()
|
||||
.Setup(s => s.GetMovieFolder(_fakeMovie, null))
|
||||
.Returns(_fakeMovie.Title);
|
||||
|
||||
var series = Subject.AddMovie(_fakeMovie);
|
||||
|
||||
series.Path.Should().NotBeNull();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.MovieTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class MovieTitleSlugValidatorFixture : CoreTest<MovieTitleSlugValidator>
|
||||
{
|
||||
private List<Movie> _movies;
|
||||
private TestValidator<Movie> _validator;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_movies = Builder<Movie>.CreateListOfSize(1)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
_validator = new TestValidator<Movie>
|
||||
{
|
||||
v => v.RuleFor(s => s.TitleSlug).SetValidator(Subject)
|
||||
};
|
||||
|
||||
Mocker.GetMock<IMovieService>()
|
||||
.Setup(s => s.GetAllMovies())
|
||||
.Returns(_movies);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_be_valid_if_there_is_an_existing_movie_with_the_same_title_slug()
|
||||
{
|
||||
var movie = Builder<Movie>.CreateNew()
|
||||
.With(s => s.Id = 100)
|
||||
.With(s => s.TitleSlug = _movies.First().TitleSlug)
|
||||
.Build();
|
||||
|
||||
_validator.Validate(movie).IsValid.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_valid_if_there_is_not_an_existing_movie_with_the_same_title_slug()
|
||||
{
|
||||
var movie = Builder<Movie>.CreateNew()
|
||||
.With(s => s.TitleSlug = "MyTitleSlug")
|
||||
.Build();
|
||||
|
||||
_validator.Validate(movie).IsValid.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_valid_if_there_is_an_existing_movie_with_a_null_title_slug()
|
||||
{
|
||||
_movies.First().TitleSlug = null;
|
||||
|
||||
var movie = Builder<Movie>.CreateNew()
|
||||
.With(s => s.TitleSlug = "MyTitleSlug")
|
||||
.Build();
|
||||
|
||||
_validator.Validate(movie).IsValid.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_valid_when_updating_an_existing_movie()
|
||||
{
|
||||
_validator.Validate(_movies.First().JsonClone()).IsValid.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using Moq;
|
||||
@ -279,7 +279,7 @@ public void should_add_new_movies_from_single_list_to_library()
|
||||
|
||||
Subject.Execute(_command);
|
||||
|
||||
Mocker.GetMock<IMovieService>()
|
||||
Mocker.GetMock<IAddMovieService>()
|
||||
.Verify(v => v.AddMovies(It.Is<List<Movie>>(s => s.Count == 5)), Times.Once());
|
||||
}
|
||||
|
||||
@ -293,7 +293,7 @@ public void should_add_new_movies_from_multiple_list_to_library()
|
||||
|
||||
Subject.Execute(_command);
|
||||
|
||||
Mocker.GetMock<IMovieService>()
|
||||
Mocker.GetMock<IAddMovieService>()
|
||||
.Verify(v => v.AddMovies(It.Is<List<Movie>>(s => s.Count == 8)), Times.Once());
|
||||
}
|
||||
|
||||
@ -307,7 +307,7 @@ public void should_add_new_movies_from_enabled_lists_to_library()
|
||||
|
||||
Subject.Execute(_command);
|
||||
|
||||
Mocker.GetMock<IMovieService>()
|
||||
Mocker.GetMock<IAddMovieService>()
|
||||
.Verify(v => v.AddMovies(It.Is<List<Movie>>(s => s.Count == 5)), Times.Once());
|
||||
}
|
||||
|
||||
@ -323,7 +323,7 @@ public void should_not_add_duplicate_movies_from_seperate_lists()
|
||||
|
||||
Subject.Execute(_command);
|
||||
|
||||
Mocker.GetMock<IMovieService>()
|
||||
Mocker.GetMock<IAddMovieService>()
|
||||
.Verify(v => v.AddMovies(It.Is<List<Movie>>(s => s.Count == 7)), Times.Once());
|
||||
}
|
||||
|
||||
@ -341,7 +341,7 @@ public void should_not_add_movie_from_on_exclusion_list()
|
||||
|
||||
Subject.Execute(_command);
|
||||
|
||||
Mocker.GetMock<IMovieService>()
|
||||
Mocker.GetMock<IAddMovieService>()
|
||||
.Verify(v => v.AddMovies(It.Is<List<Movie>>(s => s.Count == 7 && !s.Any(m => m.TmdbId == _moviesList2[0].TmdbId))), Times.Once());
|
||||
}
|
||||
|
||||
@ -359,7 +359,7 @@ public void should_not_add_movie_that_exists_in_library()
|
||||
|
||||
Subject.Execute(_command);
|
||||
|
||||
Mocker.GetMock<IMovieService>()
|
||||
Mocker.GetMock<IAddMovieService>()
|
||||
.Verify(v => v.AddMovies(It.Is<List<Movie>>(s => s.Count == 7 && !s.Any(m => m.TmdbId == _moviesList2[0].TmdbId))), Times.Once());
|
||||
}
|
||||
|
||||
|
117
src/NzbDrone.Core/Movies/AddMovieService.cs
Normal file
117
src/NzbDrone.Core/Movies/AddMovieService.cs
Normal file
@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnsureThat;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.Movies
|
||||
{
|
||||
public interface IAddMovieService
|
||||
{
|
||||
Movie AddMovie(Movie newMovie);
|
||||
List<Movie> AddMovies(List<Movie> newMovies);
|
||||
}
|
||||
|
||||
public class AddMovieService : IAddMovieService
|
||||
{
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IProvideMovieInfo _movieInfo;
|
||||
private readonly IBuildFileNames _fileNameBuilder;
|
||||
private readonly IAddMovieValidator _addMovieValidator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public AddMovieService(IMovieService movieService,
|
||||
IProvideMovieInfo movieInfo,
|
||||
IBuildFileNames fileNameBuilder,
|
||||
IAddMovieValidator addMovieValidator,
|
||||
Logger logger)
|
||||
{
|
||||
_movieService = movieService;
|
||||
_movieInfo = movieInfo;
|
||||
_fileNameBuilder = fileNameBuilder;
|
||||
_addMovieValidator = addMovieValidator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Movie AddMovie(Movie newMovie)
|
||||
{
|
||||
Ensure.That(newMovie, () => newMovie).IsNotNull();
|
||||
|
||||
newMovie = AddSkyhookData(newMovie);
|
||||
newMovie = SetPropertiesAndValidate(newMovie);
|
||||
|
||||
_logger.Info("Adding Movie {0} Path: [{1}]", newMovie, newMovie.Path);
|
||||
_movieService.AddMovie(newMovie);
|
||||
|
||||
return newMovie;
|
||||
}
|
||||
|
||||
public List<Movie> AddMovies(List<Movie> newMovies)
|
||||
{
|
||||
var added = DateTime.UtcNow;
|
||||
var moviesToAdd = new List<Movie>();
|
||||
|
||||
foreach (var m in newMovies)
|
||||
{
|
||||
// TODO: Verify if adding skyhook data will be slow
|
||||
var movie = AddSkyhookData(m);
|
||||
movie = SetPropertiesAndValidate(movie);
|
||||
movie.Added = added;
|
||||
moviesToAdd.Add(movie);
|
||||
}
|
||||
|
||||
return _movieService.AddMovies(moviesToAdd);
|
||||
}
|
||||
|
||||
private Movie AddSkyhookData(Movie newMovie)
|
||||
{
|
||||
Movie movie;
|
||||
|
||||
try
|
||||
{
|
||||
movie = _movieInfo.GetMovieInfo(newMovie.TmdbId, true).Item1;
|
||||
}
|
||||
catch (MovieNotFoundException)
|
||||
{
|
||||
_logger.Error("TmdbId {1} was not found, it may have been removed from TMDb.", newMovie.TmdbId);
|
||||
|
||||
throw new ValidationException(new List<ValidationFailure>
|
||||
{
|
||||
new ValidationFailure("TmdbId", "A movie with this ID was not found", newMovie.TmdbId)
|
||||
});
|
||||
}
|
||||
|
||||
movie.ApplyChanges(newMovie);
|
||||
|
||||
return movie;
|
||||
}
|
||||
|
||||
private Movie SetPropertiesAndValidate(Movie newMovie)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(newMovie.Path))
|
||||
{
|
||||
var folderName = _fileNameBuilder.GetMovieFolder(newMovie);
|
||||
newMovie.Path = Path.Combine(newMovie.RootFolderPath, folderName);
|
||||
}
|
||||
|
||||
newMovie.CleanTitle = newMovie.Title.CleanSeriesTitle();
|
||||
newMovie.SortTitle = MovieTitleNormalizer.Normalize(newMovie.Title, newMovie.TmdbId);
|
||||
newMovie.Added = DateTime.UtcNow;
|
||||
|
||||
var validationResult = _addMovieValidator.Validate(newMovie);
|
||||
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
return newMovie;
|
||||
}
|
||||
}
|
||||
}
|
28
src/NzbDrone.Core/Movies/AddMovieValidator.cs
Normal file
28
src/NzbDrone.Core/Movies/AddMovieValidator.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Core.Validation.Paths;
|
||||
|
||||
namespace NzbDrone.Core.Movies
|
||||
{
|
||||
public interface IAddMovieValidator
|
||||
{
|
||||
ValidationResult Validate(Movie instance);
|
||||
}
|
||||
|
||||
public class AddMovieValidator : AbstractValidator<Movie>, IAddMovieValidator
|
||||
{
|
||||
public AddMovieValidator(RootFolderValidator rootFolderValidator,
|
||||
MoviePathValidator moviePathValidator,
|
||||
MovieAncestorValidator movieAncestorValidator,
|
||||
MovieTitleSlugValidator movieTitleSlugValidator)
|
||||
{
|
||||
RuleFor(c => c.Path).Cascade(CascadeMode.StopOnFirstFailure)
|
||||
.IsValidPath()
|
||||
.SetValidator(rootFolderValidator)
|
||||
.SetValidator(moviePathValidator)
|
||||
.SetValidator(movieAncestorValidator);
|
||||
|
||||
RuleFor(c => c.TitleSlug).SetValidator(movieTitleSlugValidator);
|
||||
}
|
||||
}
|
||||
}
|
@ -94,20 +94,6 @@ public PagingSpec<Movie> Paged(PagingSpec<Movie> pagingSpec)
|
||||
|
||||
public Movie AddMovie(Movie newMovie)
|
||||
{
|
||||
Ensure.That(newMovie, () => newMovie).IsNotNull();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(newMovie.Path))
|
||||
{
|
||||
var folderName = _fileNameBuilder.GetMovieFolder(newMovie);
|
||||
newMovie.Path = Path.Combine(newMovie.RootFolderPath, folderName);
|
||||
}
|
||||
|
||||
_logger.Info("Adding Movie {0} Path: [{1}]", newMovie, newMovie.Path);
|
||||
|
||||
newMovie.CleanTitle = newMovie.Title.CleanSeriesTitle();
|
||||
newMovie.SortTitle = MovieTitleNormalizer.Normalize(newMovie.Title, newMovie.TmdbId);
|
||||
newMovie.Added = DateTime.UtcNow;
|
||||
|
||||
_movieRepository.Insert(newMovie);
|
||||
_eventAggregator.PublishEvent(new MovieAddedEvent(GetMovie(newMovie.Id)));
|
||||
|
||||
@ -116,33 +102,7 @@ public Movie AddMovie(Movie newMovie)
|
||||
|
||||
public List<Movie> AddMovies(List<Movie> newMovies)
|
||||
{
|
||||
newMovies.ForEach(m => Ensure.That(m, () => m).IsNotNull());
|
||||
|
||||
newMovies.ForEach(m =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(m.Path))
|
||||
{
|
||||
var folderName = _fileNameBuilder.GetMovieFolder(m);
|
||||
m.Path = Path.Combine(m.RootFolderPath, folderName);
|
||||
}
|
||||
|
||||
m.CleanTitle = m.Title.CleanSeriesTitle();
|
||||
m.SortTitle = MovieTitleNormalizer.Normalize(m.Title, m.TmdbId);
|
||||
m.Added = DateTime.UtcNow;
|
||||
});
|
||||
|
||||
var potentialMovieCount = newMovies.Count;
|
||||
|
||||
newMovies = newMovies.DistinctBy(movie => movie.TmdbId).ToList(); // Ensure we don't add the same movie twice
|
||||
|
||||
var existingMovies = FindByTmdbId(newMovies.Select(x => x.TmdbId).ToList());
|
||||
|
||||
newMovies = newMovies.ExceptBy(n => n.TmdbId, existingMovies, e => e.TmdbId, EqualityComparer<int>.Default).ToList(); // Ensure we don't add a movie that already exists
|
||||
|
||||
_movieRepository.InsertMany(newMovies);
|
||||
|
||||
_logger.Debug("Adding {0} movies, {1} duplicates detected and skipped", newMovies.Count, potentialMovieCount - newMovies.Count);
|
||||
|
||||
_eventAggregator.PublishEvent(new MoviesImportedEvent(newMovies.Select(s => s.Id).ToList()));
|
||||
|
||||
return newMovies;
|
||||
@ -161,13 +121,13 @@ public Movie FindByTitle(string title, int year)
|
||||
private Movie FindByTitle(string cleanTitle, int? year)
|
||||
{
|
||||
cleanTitle = cleanTitle.ToLowerInvariant();
|
||||
string cleanTitleWithRomanNumbers = cleanTitle;
|
||||
string cleanTitleWithArabicNumbers = cleanTitle;
|
||||
var cleanTitleWithRomanNumbers = cleanTitle;
|
||||
var cleanTitleWithArabicNumbers = cleanTitle;
|
||||
|
||||
foreach (ArabicRomanNumeral arabicRomanNumeral in RomanNumeralParser.GetArabicRomanNumeralsMapping())
|
||||
foreach (var arabicRomanNumeral in RomanNumeralParser.GetArabicRomanNumeralsMapping())
|
||||
{
|
||||
string arabicNumber = arabicRomanNumeral.ArabicNumeralAsString;
|
||||
string romanNumber = arabicRomanNumeral.RomanNumeral;
|
||||
var arabicNumber = arabicRomanNumeral.ArabicNumeralAsString;
|
||||
var romanNumber = arabicRomanNumeral.RomanNumeral;
|
||||
cleanTitleWithRomanNumbers = cleanTitleWithRomanNumbers.Replace(arabicNumber, romanNumber);
|
||||
cleanTitleWithArabicNumbers = cleanTitleWithArabicNumbers.Replace(romanNumber, arabicNumber);
|
||||
}
|
||||
|
44
src/NzbDrone.Core/Movies/MovieTitleSlugValidator.cs
Normal file
44
src/NzbDrone.Core/Movies/MovieTitleSlugValidator.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using System.Linq;
|
||||
using FluentValidation.Validators;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Core.Movies
|
||||
{
|
||||
public class MovieTitleSlugValidator : PropertyValidator
|
||||
{
|
||||
private readonly IMovieService _movieService;
|
||||
|
||||
public MovieTitleSlugValidator(IMovieService movieService)
|
||||
: base("Title slug '{slug}' is in use by movie '{movieTitle}'")
|
||||
{
|
||||
_movieService = movieService;
|
||||
}
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
if (context.PropertyValue == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
dynamic instance = context.ParentContext.InstanceToValidate;
|
||||
var instanceId = (int)instance.Id;
|
||||
var slug = context.PropertyValue.ToString();
|
||||
|
||||
var conflictingMovie = _movieService.GetAllMovies()
|
||||
.FirstOrDefault(s => s.TitleSlug.IsNotNullOrWhiteSpace() &&
|
||||
s.TitleSlug.Equals(context.PropertyValue.ToString()) &&
|
||||
s.Id != instanceId);
|
||||
|
||||
if (conflictingMovie == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
context.MessageFormatter.AppendArgument("slug", slug);
|
||||
context.MessageFormatter.AppendArgument("movieTitle", conflictingMovie.Title);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@ -21,12 +21,14 @@ public class NetImportSearchService : IFetchNetImport, IExecute<NetImportSyncCom
|
||||
private readonly Logger _logger;
|
||||
private readonly INetImportFactory _netImportFactory;
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IAddMovieService _addMovieService;
|
||||
private readonly ISearchForNewMovie _movieSearch;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IImportExclusionsService _exclusionService;
|
||||
|
||||
public NetImportSearchService(INetImportFactory netImportFactory,
|
||||
IMovieService movieService,
|
||||
IAddMovieService addMovieService,
|
||||
ISearchForNewMovie movieSearch,
|
||||
IConfigService configService,
|
||||
IImportExclusionsService exclusionService,
|
||||
@ -34,6 +36,7 @@ public NetImportSearchService(INetImportFactory netImportFactory,
|
||||
{
|
||||
_netImportFactory = netImportFactory;
|
||||
_movieService = movieService;
|
||||
_addMovieService = addMovieService;
|
||||
_movieSearch = movieSearch;
|
||||
_exclusionService = exclusionService;
|
||||
_logger = logger;
|
||||
@ -151,7 +154,7 @@ public void Execute(NetImportSyncCommand message)
|
||||
_logger.Info($"Adding {moviesToAdd.Count()} movies from your auto enabled lists to library");
|
||||
}
|
||||
|
||||
_movieService.AddMovies(moviesToAdd);
|
||||
_addMovieService.AddMovies(moviesToAdd);
|
||||
}
|
||||
|
||||
private void CleanLibrary(List<Movie> movies)
|
||||
|
@ -8,21 +8,21 @@ namespace Radarr.Api.V3.Movies
|
||||
{
|
||||
public class MovieImportModule : RadarrRestModule<MovieResource>
|
||||
{
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IAddMovieService _addMovieService;
|
||||
|
||||
public MovieImportModule(IMovieService movieService)
|
||||
public MovieImportModule(IAddMovieService addMovieService)
|
||||
: base("/movie/import")
|
||||
{
|
||||
_movieService = movieService;
|
||||
_addMovieService = addMovieService;
|
||||
Post("/", x => Import());
|
||||
}
|
||||
|
||||
private object Import()
|
||||
{
|
||||
var resource = Request.Body.FromJson<List<MovieResource>>();
|
||||
var newSeries = resource.ToModel();
|
||||
var newMovies = resource.ToModel();
|
||||
|
||||
return _movieService.AddMovies(newSeries).ToResource();
|
||||
return _addMovieService.AddMovies(newMovies).ToResource();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,13 +29,15 @@ public class MovieModule : RadarrRestModuleWithSignalR<MovieResource, Movie>,
|
||||
IHandle<MovieRenamedEvent>,
|
||||
IHandle<MediaCoversUpdatedEvent>
|
||||
{
|
||||
protected readonly IMovieService _moviesService;
|
||||
private readonly IMovieService _moviesService;
|
||||
private readonly IAddMovieService _addMovieService;
|
||||
private readonly IMapCoversToLocal _coverMapper;
|
||||
private readonly IManageCommandQueue _commandQueueManager;
|
||||
private readonly IUpgradableSpecification _qualityUpgradableSpecification;
|
||||
|
||||
public MovieModule(IBroadcastSignalRMessage signalRBroadcaster,
|
||||
IMovieService moviesService,
|
||||
IAddMovieService addMovieService,
|
||||
IMapCoversToLocal coverMapper,
|
||||
IManageCommandQueue commandQueueManager,
|
||||
IUpgradableSpecification qualityUpgradableSpecification,
|
||||
@ -50,6 +52,7 @@ public MovieModule(IBroadcastSignalRMessage signalRBroadcaster,
|
||||
: base(signalRBroadcaster)
|
||||
{
|
||||
_moviesService = moviesService;
|
||||
_addMovieService = addMovieService;
|
||||
_qualityUpgradableSpecification = qualityUpgradableSpecification;
|
||||
_coverMapper = coverMapper;
|
||||
_commandQueueManager = commandQueueManager;
|
||||
@ -126,7 +129,7 @@ protected MovieResource MapToResource(Movie movies)
|
||||
|
||||
private int AddMovie(MovieResource moviesResource)
|
||||
{
|
||||
var movie = _moviesService.AddMovie(moviesResource.ToModel());
|
||||
var movie = _addMovieService.AddMovie(moviesResource.ToModel());
|
||||
|
||||
return movie.Id;
|
||||
}
|
||||
|
@ -1,34 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Nancy;
|
||||
using Nancy.Extensions;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
using NzbDrone.Core.Movies;
|
||||
using Radarr.Api.V3.Movies;
|
||||
using Radarr.Http.Extensions;
|
||||
|
||||
namespace Radarr.Api.V3.NetImport
|
||||
{
|
||||
public class ListImportModule : RadarrV3Module
|
||||
{
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly ISearchForNewMovie _movieSearch;
|
||||
|
||||
public ListImportModule(IMovieService movieService, ISearchForNewMovie movieSearch)
|
||||
: base("/movie/import")
|
||||
{
|
||||
_movieService = movieService;
|
||||
_movieSearch = movieSearch;
|
||||
Put("/", movie => SaveAll());
|
||||
}
|
||||
|
||||
private object SaveAll()
|
||||
{
|
||||
var resources = Request.Body.FromJson<List<MovieResource>>();
|
||||
|
||||
var movies = resources.Select(movieResource => _movieSearch.MapMovieToTmdbMovie(movieResource.ToModel())).Where(m => m != null).DistinctBy(m => m.TmdbId).ToList();
|
||||
|
||||
return ResponseWithCode(_movieService.AddMovies(movies).ToResource(), HttpStatusCode.Accepted);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user