parent
8b06df1b1a
commit
93d27c70c4
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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,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