From d76423a30574e418bfd9090be66adc959df3206b Mon Sep 17 00:00:00 2001 From: Qstick Date: Sat, 2 Nov 2019 15:32:05 -0400 Subject: [PATCH] New: Scrape Cast/Crew/Collection Data on Movie Refresh --- src/NzbDrone.Api/Movies/MovieLookupModule.cs | 2 +- .../SkyHook/SkyHookProxyFixture.cs | 2 +- .../CreditTests/CreditServiceFixture.cs | 99 +++++++++++++++++++ .../Radarr.Core.Test.csproj | 6 +- .../Migration/164_movie_collections_crew.cs | 28 ++++++ src/NzbDrone.Core/Datastore/TableMapping.cs | 6 +- .../MetadataSource/IProvideMovieInfo.cs | 3 +- .../SkyHook/Resource/ActorResource.cs | 9 -- .../SkyHook/Resource/TMDBResources.cs | 32 +++++- .../MetadataSource/SkyHook/SkyHookProxy.cs | 86 +++++++++------- src/NzbDrone.Core/Movies/Credits/Credit.cs | 30 ++++++ .../Movies/Credits/CreditRepository.cs | 24 +++++ .../Movies/Credits/CreditService.cs | 90 +++++++++++++++++ src/NzbDrone.Core/Movies/Movie.cs | 5 +- .../Movies/{Actor.cs => MovieCollection.cs} | 8 +- .../Movies/RefreshMovieService.cs | 13 ++- src/Radarr.Api.V3/Credits/CreditModule.cs | 39 ++++++++ src/Radarr.Api.V3/Credits/CreditResource.cs | 80 +++++++++++++++ src/Radarr.Api.V3/Movies/MovieLookupModule.cs | 2 +- src/Radarr.Api.V3/Movies/MovieResource.cs | 4 +- 20 files changed, 500 insertions(+), 68 deletions(-) create mode 100644 src/NzbDrone.Core.Test/MovieTests/CreditTests/CreditServiceFixture.cs create mode 100644 src/NzbDrone.Core/Datastore/Migration/164_movie_collections_crew.cs delete mode 100644 src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ActorResource.cs create mode 100644 src/NzbDrone.Core/Movies/Credits/Credit.cs create mode 100644 src/NzbDrone.Core/Movies/Credits/CreditRepository.cs create mode 100644 src/NzbDrone.Core/Movies/Credits/CreditService.cs rename src/NzbDrone.Core/Movies/{Actor.cs => MovieCollection.cs} (61%) create mode 100644 src/Radarr.Api.V3/Credits/CreditModule.cs create mode 100644 src/Radarr.Api.V3/Credits/CreditResource.cs diff --git a/src/NzbDrone.Api/Movies/MovieLookupModule.cs b/src/NzbDrone.Api/Movies/MovieLookupModule.cs index 1829887fc..d60b5bee5 100644 --- a/src/NzbDrone.Api/Movies/MovieLookupModule.cs +++ b/src/NzbDrone.Api/Movies/MovieLookupModule.cs @@ -28,7 +28,7 @@ namespace NzbDrone.Api.Movies int tmdbId = -1; if (int.TryParse(Request.Query.tmdbId, out tmdbId)) { - var result = _movieInfo.GetMovieInfo(tmdbId, null, true); + var result = _movieInfo.GetMovieInfo(tmdbId, null, true).Item1; return result.ToResource(); } diff --git a/src/NzbDrone.Core.Test/MetadataSource/SkyHook/SkyHookProxyFixture.cs b/src/NzbDrone.Core.Test/MetadataSource/SkyHook/SkyHookProxyFixture.cs index f7306cabe..269bf9e54 100644 --- a/src/NzbDrone.Core.Test/MetadataSource/SkyHook/SkyHookProxyFixture.cs +++ b/src/NzbDrone.Core.Test/MetadataSource/SkyHook/SkyHookProxyFixture.cs @@ -22,7 +22,7 @@ namespace NzbDrone.Core.Test.MetadataSource.SkyHook [TestCase(70981, "Prometheus")] public void should_be_able_to_get_movie_detail(int tmdbId, string title) { - var details = Subject.GetMovieInfo(tmdbId); + var details = Subject.GetMovieInfo(tmdbId, null, false).Item1; ValidateMovie(details); diff --git a/src/NzbDrone.Core.Test/MovieTests/CreditTests/CreditServiceFixture.cs b/src/NzbDrone.Core.Test/MovieTests/CreditTests/CreditServiceFixture.cs new file mode 100644 index 000000000..00a276e5a --- /dev/null +++ b/src/NzbDrone.Core.Test/MovieTests/CreditTests/CreditServiceFixture.cs @@ -0,0 +1,99 @@ +using System.Collections.Generic; +using System.Linq; +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Common.Extensions; +using NzbDrone.Core.Movies; +using NzbDrone.Core.Movies.Credits; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.MovieTests.AlternativeTitleServiceTests +{ + [TestFixture] + public class CreditServiceFixture : CoreTest + { + private Credit _credit1; + private Credit _credit2; + private Credit _credit3; + + private Movie _movie; + + [SetUp] + public void Setup() + { + var credits = Builder.CreateListOfSize(3) + .All() + .With(t => t.MovieId = 0).Build(); + + _credit1 = credits[0]; + _credit2 = credits[1]; + _credit3 = credits[2]; + + _movie = Builder.CreateNew().With(m => m.Id = 1).Build(); + } + + private void GivenExistingCredits(params Credit[] credits) + { + Mocker.GetMock().Setup(r => r.FindByMovieId(_movie.Id)) + .Returns(credits.ToList()); + } + + [Test] + public void should_update_insert_remove_titles() + { + var titles = new List { _credit2, _credit3 }; + var updates = new List { _credit2 }; + var deletes = new List { _credit1 }; + var inserts = new List { _credit3 }; + + GivenExistingCredits(_credit1, _credit2); + + Subject.UpdateCredits(titles, _movie); + + Mocker.GetMock().Verify(r => r.InsertMany(inserts), Times.Once()); + Mocker.GetMock().Verify(r => r.UpdateMany(updates), Times.Once()); + Mocker.GetMock().Verify(r => r.DeleteMany(deletes), Times.Once()); + } + + [Test] + public void should_not_insert_duplicates() + { + GivenExistingCredits(); + var credits = new List { _credit1, _credit1 }; + var inserts = new List { _credit1 }; + + Subject.UpdateCredits(credits, _movie); + + Mocker.GetMock().Verify(r => r.InsertMany(inserts), Times.Once()); + } + + [Test] + public void should_update_movie_id() + { + GivenExistingCredits(); + var titles = new List { _credit1, _credit2 }; + + Subject.UpdateCredits(titles, _movie); + + _credit1.MovieId.Should().Be(_movie.Id); + _credit2.MovieId.Should().Be(_movie.Id); + } + + [Test] + public void should_update_with_correct_id() + { + var existingCredit = Builder.CreateNew().With(t => t.Id = 2).Build(); + + GivenExistingCredits(existingCredit); + + var updateCredit = existingCredit.JsonClone(); + updateCredit.Id = 0; + + Subject.UpdateCredits(new List { updateCredit }, _movie); + + Mocker.GetMock().Verify(r => r.UpdateMany(It.Is>(list => list.First().Id == existingCredit.Id)), Times.Once()); + } + } +} diff --git a/src/NzbDrone.Core.Test/Radarr.Core.Test.csproj b/src/NzbDrone.Core.Test/Radarr.Core.Test.csproj index 50e0e2b8e..61856c1d5 100644 --- a/src/NzbDrone.Core.Test/Radarr.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/Radarr.Core.Test.csproj @@ -19,7 +19,7 @@ - + @@ -30,10 +30,10 @@ PreserveNewest - + - + diff --git a/src/NzbDrone.Core/Datastore/Migration/164_movie_collections_crew.cs b/src/NzbDrone.Core/Datastore/Migration/164_movie_collections_crew.cs new file mode 100644 index 000000000..a1f4558cc --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/164_movie_collections_crew.cs @@ -0,0 +1,28 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(164)] + public class movie_collections_crew : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Alter.Table("Movies").AddColumn("Collection").AsString().Nullable(); + Delete.Column("Actors").FromTable("Movies"); + + Create.TableForModel("Credits").WithColumn("MovieId").AsInt32() + .WithColumn("CreditTmdbId").AsString().Unique() + .WithColumn("PersonTmdbId").AsInt32() + .WithColumn("Name").AsString() + .WithColumn("Images").AsString() + .WithColumn("Character").AsString().Nullable() + .WithColumn("Order").AsInt32() + .WithColumn("Job").AsString().Nullable() + .WithColumn("Department").AsString().Nullable() + .WithColumn("Type").AsInt32(); + + Create.Index().OnTable("Credits").OnColumn("MovieId"); + } + } +} diff --git a/src/NzbDrone.Core/Datastore/TableMapping.cs b/src/NzbDrone.Core/Datastore/TableMapping.cs index d9d1f6349..c17e6fd93 100644 --- a/src/NzbDrone.Core/Datastore/TableMapping.cs +++ b/src/NzbDrone.Core/Datastore/TableMapping.cs @@ -22,6 +22,7 @@ using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Movies; using NzbDrone.Core.Movies.AlternativeTitles; +using NzbDrone.Core.Movies.Credits; using NzbDrone.Core.NetImport; using NzbDrone.Core.NetImport.ImportExclusions; using NzbDrone.Core.Notifications; @@ -96,11 +97,12 @@ namespace NzbDrone.Core.Datastore .Ignore(f => f.Path); Mapper.Entity("Movies").RegisterModel() - .Ignore(s => s.RootFolderPath) - .Ignore(m => m.Actors); + .Ignore(s => s.RootFolderPath); Mapper.Entity("AlternativeTitles").RegisterModel(); + Mapper.Entity("Credits").RegisterModel(); + Mapper.Entity("ImportExclusions").RegisterModel(); Mapper.Entity("QualityDefinitions").RegisterModel() diff --git a/src/NzbDrone.Core/MetadataSource/IProvideMovieInfo.cs b/src/NzbDrone.Core/MetadataSource/IProvideMovieInfo.cs index c86d2f55a..8ef0dcaa4 100644 --- a/src/NzbDrone.Core/MetadataSource/IProvideMovieInfo.cs +++ b/src/NzbDrone.Core/MetadataSource/IProvideMovieInfo.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using NzbDrone.Core.Movies; +using NzbDrone.Core.Movies.Credits; using NzbDrone.Core.Profiles; namespace NzbDrone.Core.MetadataSource @@ -8,7 +9,7 @@ namespace NzbDrone.Core.MetadataSource public interface IProvideMovieInfo { Movie GetMovieInfo(string imdbId); - Movie GetMovieInfo(int tmdbId, Profile profile, bool hasPreDBEntry); + Tuple> GetMovieInfo(int tmdbId, Profile profile, bool hasPreDBEntry); HashSet GetChangedMovies(DateTime startTime); } } diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ActorResource.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ActorResource.cs deleted file mode 100644 index 0fdf22c55..000000000 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ActorResource.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace NzbDrone.Core.MetadataSource.SkyHook.Resource -{ - public class ActorResource - { - public string Name { get; set; } - public string Character { get; set; } - public string Image { get; set; } - } -} diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/TMDBResources.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/TMDBResources.cs index 6c4c3c476..fa0b409b2 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/TMDBResources.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/TMDBResources.cs @@ -41,7 +41,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource { public bool adult { get; set; } public string backdrop_path { get; set; } - public Belongs_To_Collection belongs_to_collection { get; set; } + public CollectionResource belongs_to_collection { get; set; } public int? status_code { get; set; } public string status_message { get; set; } public int budget { get; set; } @@ -69,6 +69,8 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource public AlternativeTitles alternative_titles { get; set; } public ReleaseDatesResource release_dates { get; set; } public VideosResource videos { get; set; } + + public CreditsResource credits { get; set; } } public class ReleaseDatesResource @@ -76,6 +78,12 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource public List results { get; set; } } + public class CreditsResource + { + public List Cast { get; set; } + public List Crew { get; set; } + } + public class ReleaseDate { public string certification { get; set; } @@ -91,7 +99,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource public List release_dates { get; set; } } - public class Belongs_To_Collection + public class CollectionResource { public int id { get; set; } public string name { get; set; } @@ -139,6 +147,26 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource public List