From dd8af0ad8c0d35d661f8dc7d66b847b60f22dc5f Mon Sep 17 00:00:00 2001 From: Leonardo Galli Date: Fri, 27 Jan 2017 19:36:25 +0100 Subject: [PATCH] Manual Import works now! Also fixed a few bugs. --- .../NetImport/ListImportModule.cs | 34 ++ src/NzbDrone.Api/NzbDrone.Api.csproj | 1 + .../Series/FetchMovieListModule.cs | 6 +- .../SkyHook/Resource/TMDBResources.cs | 2 + .../MetadataSource/SkyHook/SkyHookProxy.cs | 19 +- .../NetImport/NetImportSearchService.cs | 6 - src/NzbDrone.Core/Tv/MovieService.cs | 30 ++ src/UI/AddMovies/List/AddFromListView.js | 26 +- .../List/AddFromListViewTemplate.hbs | 2 +- src/UI/Movies/MoviesCollection.js | 293 ++++++++++-------- 10 files changed, 264 insertions(+), 155 deletions(-) create mode 100644 src/NzbDrone.Api/NetImport/ListImportModule.cs diff --git a/src/NzbDrone.Api/NetImport/ListImportModule.cs b/src/NzbDrone.Api/NetImport/ListImportModule.cs new file mode 100644 index 000000000..f1d81aefd --- /dev/null +++ b/src/NzbDrone.Api/NetImport/ListImportModule.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using Nancy; +using Nancy.Extensions; +using NzbDrone.Api.Extensions; +using NzbDrone.Api.Movie; +using NzbDrone.Core.MetadataSource; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Api.NetImport +{ + public class ListImportModule : NzbDroneApiModule + { + 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 Response SaveAll() + { + var resources = Request.Body.FromJson>(); + + var Movies = resources.Select(MovieResource => _movieSearch.MapMovieToTmdbMovie(MovieResource.ToModel())).Where(m => m != null).DistinctBy(m => m.TmdbId).ToList(); + + return _movieService.AddMovies(Movies).ToResource().AsResponse(HttpStatusCode.Accepted); + } + } +} diff --git a/src/NzbDrone.Api/NzbDrone.Api.csproj b/src/NzbDrone.Api/NzbDrone.Api.csproj index 097933cdc..1f0542788 100644 --- a/src/NzbDrone.Api/NzbDrone.Api.csproj +++ b/src/NzbDrone.Api/NzbDrone.Api.csproj @@ -123,6 +123,7 @@ + diff --git a/src/NzbDrone.Api/Series/FetchMovieListModule.cs b/src/NzbDrone.Api/Series/FetchMovieListModule.cs index 52475d0cb..871ebd7bc 100644 --- a/src/NzbDrone.Api/Series/FetchMovieListModule.cs +++ b/src/NzbDrone.Api/Series/FetchMovieListModule.cs @@ -28,7 +28,7 @@ namespace NzbDrone.Api.Movie List realResults = new List(); - foreach (var movie in results) + /*foreach (var movie in results) { var mapped = _movieSearch.MapMovieToTmdbMovie(movie); @@ -36,9 +36,9 @@ namespace NzbDrone.Api.Movie { realResults.Add(mapped); } - } + }*/ - return MapToResource(realResults).AsResponse(); + return MapToResource(results).AsResponse(); } diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/TMDBResources.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/TMDBResources.cs index 469e72776..182992c38 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/TMDBResources.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/TMDBResources.cs @@ -42,6 +42,8 @@ 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 int? status_code { get; set; } + public string status_message { get; set; } public int budget { get; set; } public Genre[] genres { get; set; } public string homepage { get; set; } diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs index b6151d46b..70d3dda89 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs @@ -84,6 +84,18 @@ namespace NzbDrone.Core.MetadataSource.SkyHook var resource = response.Resource; + if (resource.status_message != null) + { + if (resource.status_code == 34) + { + _logger.Warn("Movie with TmdbId {0} could not be found. This is probably the case when the movie was deleted from TMDB.", TmdbId); + return null; + } + + _logger.Warn(resource.status_message); + return null; + } + var movie = new Movie(); movie.TmdbId = TmdbId; @@ -567,10 +579,9 @@ namespace NzbDrone.Core.MetadataSource.SkyHook Movie newMovie = movie; if (movie.TmdbId > 0) { - return newMovie; + newMovie = GetMovieInfo(movie.TmdbId); } - - if (movie.ImdbId.IsNotNullOrWhiteSpace()) + else if (movie.ImdbId.IsNotNullOrWhiteSpace()) { newMovie = GetMovieInfo(movie.ImdbId); } @@ -586,7 +597,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook if (newMovie == null) { - _logger.Warn("Couldn't map movie {0} to a movie on The Movie DB."); + _logger.Warn("Couldn't map movie {0} to a movie on The Movie DB. It will not be added :(", movie.Title); return null; } diff --git a/src/NzbDrone.Core/NetImport/NetImportSearchService.cs b/src/NzbDrone.Core/NetImport/NetImportSearchService.cs index 49500cd4c..e119d8b36 100644 --- a/src/NzbDrone.Core/NetImport/NetImportSearchService.cs +++ b/src/NzbDrone.Core/NetImport/NetImportSearchService.cs @@ -25,7 +25,6 @@ namespace NzbDrone.Core.NetImport private readonly IMovieService _movieService; private readonly ISearchForNewMovie _movieSearch; private readonly IRootFolderService _rootFolder; - private string defaultRootFolder; public NetImportSearchService(INetImportFactory netImportFactory, IMovieService movieService, ISearchForNewMovie movieSearch, IRootFolderService rootFolder, Logger logger) @@ -34,11 +33,6 @@ namespace NzbDrone.Core.NetImport _movieService = movieService; _movieSearch = movieSearch; _rootFolder = rootFolder; - var folder = _rootFolder.All().FirstOrDefault(); - if (folder != null) - { - defaultRootFolder = folder.Path; - } _logger = logger; } diff --git a/src/NzbDrone.Core/Tv/MovieService.cs b/src/NzbDrone.Core/Tv/MovieService.cs index 03ba48e4f..9c33e1ecd 100644 --- a/src/NzbDrone.Core/Tv/MovieService.cs +++ b/src/NzbDrone.Core/Tv/MovieService.cs @@ -21,6 +21,7 @@ namespace NzbDrone.Core.Tv Movie GetMovie(int movieId); List GetMovies(IEnumerable movieIds); Movie AddMovie(Movie newMovie); + List AddMovies(List newMovies); Movie FindByImdbId(string imdbid); Movie FindByTitle(string title); Movie FindByTitle(string title, int year); @@ -92,6 +93,35 @@ namespace NzbDrone.Core.Tv return newMovie; } + public List AddMovies(List newMovies) + { + _logger.Debug("Adding {0} movies", newMovies.Count); + + 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; + }); + + _movieRepository.InsertMany(newMovies); + + newMovies.ForEach(m => + { + _eventAggregator.PublishEvent(new MovieAddedEvent(m)); + }); + + return newMovies; + } + public Movie FindByTitle(string title) { return _movieRepository.FindByTitle(title.CleanSeriesTitle()); diff --git a/src/UI/AddMovies/List/AddFromListView.js b/src/UI/AddMovies/List/AddFromListView.js index 05d60dcad..d2605c74f 100644 --- a/src/UI/AddMovies/List/AddFromListView.js +++ b/src/UI/AddMovies/List/AddFromListView.js @@ -22,7 +22,8 @@ var MovieStatusCell = require('../../Cells/MovieStatusCell'); var MovieDownloadStatusCell = require('../../Cells/MovieDownloadStatusCell'); var DownloadedQualityCell = require('../../Cells/DownloadedQualityCell'); var MoviesCollection = require('../../Movies/MoviesCollection'); - +var Messenger = require('../../Shared/Messenger'); +require('jquery.dotdotdot'); var SchemaModal = require('../../Settings/NetImport/Add/NetImportSchemaModal'); module.exports = Marionette.Layout.extend({ @@ -35,7 +36,7 @@ module.exports = Marionette.Layout.extend({ ui : { moviesSearch : '.x-movies-search', listSelection : ".x-list-selection", - + importSelected : ".x-import-selected" }, columns : [ @@ -185,9 +186,24 @@ module.exports = Marionette.Layout.extend({ _importSelected : function() { var selected = this.importGrid.getSelectedModels(); console.log(selected); - _.each(selected, function(elem){ - elem.save(); - }) + var promise = MoviesCollection.importFromList(selected); + this.ui.importSelected.spinForPromise(promise); + this.ui.importSelected.addClass('disabled'); + + Messenger.show({ + message : "Importing {0} movies. This can take multiple minutes depending on how many movies should be imported. Don't close this browser window until it is finished!".format(selected.length), + hideOnNavigate : false, + hideAfter : 30, + type : "error" + }); + + promise.done(function() { + Messenger.show({ + message : "Imported movies from list.", + hideAfter : 8, + hideOnNavigate : true + }); + }); /*for (m in selected) { debugger; m.save() diff --git a/src/UI/AddMovies/List/AddFromListViewTemplate.hbs b/src/UI/AddMovies/List/AddFromListViewTemplate.hbs index cad2b19bc..f63c95573 100644 --- a/src/UI/AddMovies/List/AddFromListViewTemplate.hbs +++ b/src/UI/AddMovies/List/AddFromListViewTemplate.hbs @@ -9,7 +9,7 @@
- +
diff --git a/src/UI/Movies/MoviesCollection.js b/src/UI/Movies/MoviesCollection.js index 193f47ef6..779fb4a0d 100644 --- a/src/UI/Movies/MoviesCollection.js +++ b/src/UI/Movies/MoviesCollection.js @@ -10,142 +10,163 @@ var moment = require('moment'); require('../Mixins/backbone.signalr.mixin'); var Collection = PageableCollection.extend({ - url : window.NzbDrone.ApiRoot + '/movie', - model : MovieModel, - tableName : 'movie', - - state : { - sortKey : 'sortTitle', - order : 1, - pageSize : 100000, - secondarySortKey : 'sortTitle', - secondarySortOrder : -1 - }, - - mode : 'client', - - save : function() { - var self = this; - - var proxy = _.extend(new Backbone.Model(), { - id : '', - - url : self.url + '/editor', - - toJSON : function() { - return self.filter(function(model) { - return model.edited; - }); - } - }); - - this.listenTo(proxy, 'sync', function(proxyModel, models) { - this.add(models, { merge : true }); - this.trigger('save', this); - }); - - return proxy.save(); - }, - - filterModes : { - 'all' : [ - null, - null - ], - 'continuing' : [ - 'status', - 'continuing' - ], - 'ended' : [ - 'status', - 'ended' - ], - 'monitored' : [ - 'monitored', - true - ], - 'missing' : [ - 'downloaded', - false - ] - }, - - sortMappings : { - title : { - sortKey : 'sortTitle' - }, - statusWeight : { - sortValue : function(model, attr) { - if (model.getStatus() == "released") { - return 1; - } - if (model.getStatus() == "inCinemas") { - return 0; - } - return -1; - } - }, - downloadedQuality : { - sortValue : function(model, attr) { - if (model.get("movieFile")) { - return 1000-model.get("movieFile").quality.quality.id; - } - - return -1; - } - }, - nextAiring : { - sortValue : function(model, attr, order) { - var nextAiring = model.get(attr); - - if (nextAiring) { - return moment(nextAiring).unix(); - } - - if (order === 1) { - return 0; - } - - return Number.MAX_VALUE; - } - }, - status: { - sortValue : function(model, attr) { - debugger; - if (model.get("downloaded")) { - return -1; - } - return 0; - } - }, - percentOfEpisodes : { - sortValue : function(model, attr) { - var percentOfEpisodes = model.get(attr); - var episodeCount = model.get('episodeCount'); - - return percentOfEpisodes + episodeCount / 1000000; - } - }, - inCinemas : { - - sortValue : function(model, attr) { - var monthNames = ["January", "February", "March", "April", "May", "June", - "July", "August", "September", "October", "November", "December" - ]; - if (model.get("inCinemas")) { - return model.get("inCinemas"); - } - return "2100-01-01"; - } - }, - path : { - sortValue : function(model) { - var path = model.get('path'); - - return path.toLowerCase(); - } - } - } + url : window.NzbDrone.ApiRoot + '/movie', + model : MovieModel, + tableName : 'movie', + + state : { + sortKey : 'sortTitle', + order : 1, + pageSize : 100000, + secondarySortKey : 'sortTitle', + secondarySortOrder : -1 + }, + + mode : 'client', + + save : function() { + var self = this; + + var proxy = _.extend(new Backbone.Model(), { + id : '', + + url : self.url + '/editor', + + toJSON : function() { + return self.filter(function(model) { + return model.edited; + }); + } + }); + + this.listenTo(proxy, 'sync', function(proxyModel, models) { + this.add(models, { merge : true }); + this.trigger('save', this); + }); + + return proxy.save(); + }, + + importFromList : function(models) { + var self = this; + + var proxy = _.extend(new Backbone.Model(), { + id : "", + + url : self.url + "/import", + + toJSON : function() { + return models; + } + }); + + this.listenTo(proxy, "sync", function(proxyModel, models) { + this.add(models, { merge : true}); + this.trigger("save", this); + }); + + return proxy.save(); + }, + + filterModes : { + 'all' : [ + null, + null + ], + 'continuing' : [ + 'status', + 'continuing' + ], + 'ended' : [ + 'status', + 'ended' + ], + 'monitored' : [ + 'monitored', + true + ], + 'missing' : [ + 'downloaded', + false + ] + }, + + sortMappings : { + title : { + sortKey : 'sortTitle' + }, + statusWeight : { + sortValue : function(model, attr) { + if (model.getStatus() == "released") { + return 1; + } + if (model.getStatus() == "inCinemas") { + return 0; + } + return -1; + } + }, + downloadedQuality : { + sortValue : function(model, attr) { + if (model.get("movieFile")) { + return 1000-model.get("movieFile").quality.quality.id; + } + + return -1; + } + }, + nextAiring : { + sortValue : function(model, attr, order) { + var nextAiring = model.get(attr); + + if (nextAiring) { + return moment(nextAiring).unix(); + } + + if (order === 1) { + return 0; + } + + return Number.MAX_VALUE; + } + }, + status: { + sortValue : function(model, attr) { + debugger; + if (model.get("downloaded")) { + return -1; + } + return 0; + } + }, + percentOfEpisodes : { + sortValue : function(model, attr) { + var percentOfEpisodes = model.get(attr); + var episodeCount = model.get('episodeCount'); + + return percentOfEpisodes + episodeCount / 1000000; + } + }, + inCinemas : { + + sortValue : function(model, attr) { + var monthNames = ["January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" + ]; + if (model.get("inCinemas")) { + return model.get("inCinemas"); + } + return "2100-01-01"; + } + }, + path : { + sortValue : function(model) { + var path = model.get('path'); + + return path.toLowerCase(); + } + } + } }); Collection = AsFilteredCollection.call(Collection);