From 82f29cdc70a8ba9e896e8a459a65d62cdf940f62 Mon Sep 17 00:00:00 2001 From: Leonardo Galli Date: Mon, 23 Jan 2017 15:21:49 +0100 Subject: [PATCH] Add Ability to set RootFolderPath for Net Import List Fixed other things. Finished command to automatically add a movie. --- src/NzbDrone.Api/NetImport/NetImportModule.cs | 3 +- .../NetImport/NetImportResource.cs | 1 + .../Migration/123_create_netimport_table.cs | 2 + .../MetadataSource/ISearchForNewMovie.cs | 2 + .../MetadataSource/SkyHook/SkyHookProxy.cs | 35 ++++++++++++ .../CouchPotatoRequestGenerator.cs | 9 +++- .../CouchPotato/CouchPotatoSettings.cs | 9 +++- .../NetImport/HttpNetImportBase.cs | 7 ++- .../NetImport/NetImportDefinition.cs | 1 + .../NetImport/NetImportSearchService.cs | 31 +++++++++-- .../NetImport/Edit/NetImportEditView.js | 54 ++++++++++++++++++- .../Edit/NetImportEditViewTemplate.hbs | 8 +++ src/UI/Settings/NetImport/NetImportLayout.js | 4 ++ 13 files changed, 157 insertions(+), 9 deletions(-) diff --git a/src/NzbDrone.Api/NetImport/NetImportModule.cs b/src/NzbDrone.Api/NetImport/NetImportModule.cs index 0149a44e7..96faebb80 100644 --- a/src/NzbDrone.Api/NetImport/NetImportModule.cs +++ b/src/NzbDrone.Api/NetImport/NetImportModule.cs @@ -20,7 +20,7 @@ namespace NzbDrone.Api.NetImport resource.Enabled = definition.Enabled; resource.EnableAuto = definition.EnableAuto; resource.ProfileId = definition.ProfileId; - + resource.RootFolderPath = definition.RootFolderPath; } protected override void MapToModel(NetImportDefinition definition, NetImportResource resource) @@ -30,6 +30,7 @@ namespace NzbDrone.Api.NetImport definition.Enabled = resource.Enabled; definition.EnableAuto = resource.EnableAuto; definition.ProfileId = resource.ProfileId; + definition.RootFolderPath = resource.RootFolderPath; } protected override void Validate(NetImportDefinition definition, bool includeWarnings) diff --git a/src/NzbDrone.Api/NetImport/NetImportResource.cs b/src/NzbDrone.Api/NetImport/NetImportResource.cs index b65f736ad..efde1f836 100644 --- a/src/NzbDrone.Api/NetImport/NetImportResource.cs +++ b/src/NzbDrone.Api/NetImport/NetImportResource.cs @@ -6,6 +6,7 @@ namespace NzbDrone.Api.NetImport { public bool Enabled { get; set; } public bool EnableAuto { get; set; } + public string RootFolderPath { get; set; } public int ProfileId { get; set; } } } \ No newline at end of file diff --git a/src/NzbDrone.Core/Datastore/Migration/123_create_netimport_table.cs b/src/NzbDrone.Core/Datastore/Migration/123_create_netimport_table.cs index 2fa8bd65a..27d620eb9 100644 --- a/src/NzbDrone.Core/Datastore/Migration/123_create_netimport_table.cs +++ b/src/NzbDrone.Core/Datastore/Migration/123_create_netimport_table.cs @@ -1,4 +1,5 @@ using FluentMigrator; +using FluentMigrator.Expressions; using NzbDrone.Core.Datastore.Migration.Framework; namespace NzbDrone.Core.Datastore.Migration @@ -17,6 +18,7 @@ namespace NzbDrone.Core.Datastore.Migration .WithColumn("ConfigContract").AsString().Nullable() .WithColumn("Settings").AsString().Nullable() .WithColumn("EnableAuto").AsInt32() + .WithColumn("RootFolderPath").AsString() .WithColumn("ProfileId").AsInt32(); } } diff --git a/src/NzbDrone.Core/MetadataSource/ISearchForNewMovie.cs b/src/NzbDrone.Core/MetadataSource/ISearchForNewMovie.cs index d895075f9..bda58e7af 100644 --- a/src/NzbDrone.Core/MetadataSource/ISearchForNewMovie.cs +++ b/src/NzbDrone.Core/MetadataSource/ISearchForNewMovie.cs @@ -6,5 +6,7 @@ namespace NzbDrone.Core.MetadataSource public interface ISearchForNewMovie { List SearchForNewMovie(string title); + + Movie MapMovieToTmdbMovie(Movie movie); } } \ No newline at end of file diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs index f158b44fe..0d9446954 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs @@ -561,5 +561,40 @@ namespace NzbDrone.Core.MetadataSource.SkyHook return value; } + + public Movie MapMovieToTmdbMovie(Movie movie) + { + Movie newMovie = movie; + if (movie.TmdbId > 0) + { + return newMovie; + } + + if (movie.ImdbId.IsNotNullOrWhiteSpace()) + { + newMovie = GetMovieInfo(movie.ImdbId); + } + else + { + var yearStr = ""; + if (movie.Year > 1900) + { + yearStr = $" {movie.Year}"; + } + newMovie = SearchForNewMovie(movie.Title + yearStr).FirstOrDefault(); + } + + if (newMovie == null) + { + _logger.Warn("Couldn't map movie {0} to a movie on The Movie DB."); + return null; + } + + newMovie.Path = movie.Path; + newMovie.RootFolderPath = movie.RootFolderPath; + newMovie.ProfileId = movie.ProfileId; + + return newMovie; + } } } diff --git a/src/NzbDrone.Core/NetImport/CouchPotato/CouchPotatoRequestGenerator.cs b/src/NzbDrone.Core/NetImport/CouchPotato/CouchPotatoRequestGenerator.cs index 8a29dd112..0416ec5e8 100644 --- a/src/NzbDrone.Core/NetImport/CouchPotato/CouchPotatoRequestGenerator.cs +++ b/src/NzbDrone.Core/NetImport/CouchPotato/CouchPotatoRequestGenerator.cs @@ -27,7 +27,14 @@ namespace NzbDrone.Core.NetImport.CouchPotato urlBase = Settings.UrlBase.StartsWith("/") ? Settings.UrlBase : $"/{Settings.UrlBase}"; } - var request = new NetImportRequest($"{Settings.Link.Trim()}:{Settings.Port}{urlBase}/api/{Settings.ApiKey}/movie.list/?status=active", HttpAccept.Json); + var status = ""; + + if (Settings.OnlyActive) + { + status = "?status=active"; + } + + var request = new NetImportRequest($"{Settings.Link.Trim()}:{Settings.Port}{urlBase}/api/{Settings.ApiKey}/movie.list/{status}", HttpAccept.Json); yield return request; } } diff --git a/src/NzbDrone.Core/NetImport/CouchPotato/CouchPotatoSettings.cs b/src/NzbDrone.Core/NetImport/CouchPotato/CouchPotatoSettings.cs index 05744c825..fcebef860 100644 --- a/src/NzbDrone.Core/NetImport/CouchPotato/CouchPotatoSettings.cs +++ b/src/NzbDrone.Core/NetImport/CouchPotato/CouchPotatoSettings.cs @@ -14,6 +14,7 @@ namespace NzbDrone.Core.NetImport.CouchPotato Link = "http://localhost"; Port = 5050; UrlBase = ""; + OnlyActive = false; } [FieldDefinition(0, Label = "CouchPotato URL", HelpText = "Link to your CoouchPootato.")] @@ -22,10 +23,16 @@ namespace NzbDrone.Core.NetImport.CouchPotato [FieldDefinition(1, Label = "CouchPotato Port", HelpText = "Port your CoouchPootato uses.")] public int Port { get; set; } - [FieldDefinition(2, Label = "CouchPotato Url Base", HelpText = "UrlBase your CoouchPootato uses, leave blank for none")] + [FieldDefinition(2, Label = "CouchPotato Url Base", + HelpText = "UrlBase your CoouchPootato uses, leave blank for none")] public string UrlBase { get; set; } [FieldDefinition(3, Label = "CouchPotato API Key", HelpText = "CoouchPootato API Key.")] public string ApiKey { get; set; } + + [FieldDefinition(4, Label = "Only Active", HelpText = "Should only active (not yet downloaded) movies be fetched")] + public bool OnlyActive { get; set; } + } + } diff --git a/src/NzbDrone.Core/NetImport/HttpNetImportBase.cs b/src/NzbDrone.Core/NetImport/HttpNetImportBase.cs index f2aef309c..5c4448164 100644 --- a/src/NzbDrone.Core/NetImport/HttpNetImportBase.cs +++ b/src/NzbDrone.Core/NetImport/HttpNetImportBase.cs @@ -214,7 +214,12 @@ namespace NzbDrone.Core.NetImport { var response = FetchIndexerResponse(request); - return parser.ParseResponse(response).ToList(); + return parser.ParseResponse(response).ToList().Select(m => + { + m.RootFolderPath = ((NetImportDefinition) Definition).RootFolderPath; + m.ProfileId = ((NetImportDefinition) Definition).ProfileId; + return m; + }).ToList(); } protected virtual NetImportResponse FetchIndexerResponse(NetImportRequest request) diff --git a/src/NzbDrone.Core/NetImport/NetImportDefinition.cs b/src/NzbDrone.Core/NetImport/NetImportDefinition.cs index bd0628656..ae661e5b3 100644 --- a/src/NzbDrone.Core/NetImport/NetImportDefinition.cs +++ b/src/NzbDrone.Core/NetImport/NetImportDefinition.cs @@ -10,6 +10,7 @@ namespace NzbDrone.Core.NetImport public bool EnableAuto { get; set; } public int ProfileId { get; set; } public LazyLoaded Profile { get; set; } + public string RootFolderPath { get; set; } public override bool Enable => Enabled; } } diff --git a/src/NzbDrone.Core/NetImport/NetImportSearchService.cs b/src/NzbDrone.Core/NetImport/NetImportSearchService.cs index cf2f407a1..49500cd4c 100644 --- a/src/NzbDrone.Core/NetImport/NetImportSearchService.cs +++ b/src/NzbDrone.Core/NetImport/NetImportSearchService.cs @@ -3,8 +3,11 @@ using System.Collections.Generic; using System.Linq; using System.Text; using NLog; +using NzbDrone.Common.Extensions; using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.MetadataSource; +using NzbDrone.Core.RootFolders; using NzbDrone.Core.Tv; namespace NzbDrone.Core.NetImport @@ -20,14 +23,26 @@ namespace NzbDrone.Core.NetImport private readonly Logger _logger; private readonly INetImportFactory _netImportFactory; private readonly IMovieService _movieService; + private readonly ISearchForNewMovie _movieSearch; + private readonly IRootFolderService _rootFolder; + private string defaultRootFolder; - public NetImportSearchService(INetImportFactory netImportFactory, IMovieService movieService, Logger logger) + public NetImportSearchService(INetImportFactory netImportFactory, IMovieService movieService, + ISearchForNewMovie movieSearch, IRootFolderService rootFolder, Logger logger) { _netImportFactory = netImportFactory; _movieService = movieService; + _movieSearch = movieSearch; + _rootFolder = rootFolder; + var folder = _rootFolder.All().FirstOrDefault(); + if (folder != null) + { + defaultRootFolder = folder.Path; + } _logger = logger; } + public List Fetch(int listId, bool onlyEnableAuto = false) { return MovieListSearch(listId, onlyEnableAuto); @@ -65,9 +80,19 @@ namespace NzbDrone.Core.NetImport public void Execute(NetImportSyncCommand message) { - var movies = FetchAndFilter(2, false); + var movies = FetchAndFilter(0, true); - _logger.Debug("Found {0} movies on your lists not in your library", movies.Count); + _logger.Debug("Found {0} movies on your auto enabled lists not in your library", movies.Count); + + foreach (var movie in movies) + { + var mapped = _movieSearch.MapMovieToTmdbMovie(movie); + + if (mapped != null) + { + _movieService.AddMovie(mapped); + } + } } } } diff --git a/src/UI/Settings/NetImport/Edit/NetImportEditView.js b/src/UI/Settings/NetImport/Edit/NetImportEditView.js index ce97dd3b6..9c6c25101 100644 --- a/src/UI/Settings/NetImport/Edit/NetImportEditView.js +++ b/src/UI/Settings/NetImport/Edit/NetImportEditView.js @@ -7,6 +7,9 @@ var Profiles = require('../../../Profile/ProfileCollection'); var AsModelBoundView = require('../../../Mixins/AsModelBoundView'); var AsValidatedView = require('../../../Mixins/AsValidatedView'); var AsEditModalView = require('../../../Mixins/AsEditModalView'); +var RootFolders = require('../../../AddMovies/RootFolders/RootFolderCollection'); +var RootFolderLayout = require('../../../AddMovies/RootFolders/RootFolderLayout'); +var Config = require('../../../Config'); require('../../../Form/FormBuilder'); require('../../../Mixins/AutoComplete'); require('bootstrap'); @@ -14,9 +17,15 @@ require('bootstrap'); var view = Marionette.ItemView.extend({ template : 'Settings/NetImport/Edit/NetImportEditViewTemplate', + ui : { + profile : '.x-profile', + rootFolder : '.x-root-folder', + }, + events : { 'click .x-back' : '_back', - 'click .x-captcha-refresh' : '_onRefreshCaptcha' + 'click .x-captcha-refresh' : '_onRefreshCaptcha', + 'change .x-root-folder' : '_rootFolderChanged', }, _deleteView : DeleteView, @@ -24,7 +33,26 @@ var view = Marionette.ItemView.extend({ initialize : function(options) { this.targetCollection = options.targetCollection; this.templateHelpers = {}; - this.templateHelpers.profiles = Profiles.toJSON(); + + this._configureTemplateHelpers(); + this.listenTo(this.model, 'change', this.render); + this.listenTo(RootFolders, 'all', this._rootFoldersUpdated); + }, + + onRender : function() { + var defaultRoot = Config.getValue(Config.Keys.DefaultRootFolderId); + if (RootFolders.get(defaultRoot)) { + this.ui.rootFolder.val(defaultRoot); + } + }, + + _onBeforeSave : function() { + var profile = this.ui.profile.val(); + var rootFolderPath = this.ui.rootFolder.children(':selected').text(); + this.model.set({ + profileId : profile, + rootFolderPath : rootFolderPath, + }) }, _onAfterSave : function() { @@ -46,6 +74,28 @@ var view = Marionette.ItemView.extend({ require('../Add/NetImportSchemaModal').open(this.targetCollection); }, + _configureTemplateHelpers : function() { + this.templateHelpers.profiles = Profiles.toJSON(); + this.templateHelpers.rootFolders = RootFolders.toJSON(); + }, + + _rootFolderChanged : function() { + var rootFolderValue = this.ui.rootFolder.val(); + if (rootFolderValue === 'addNew') { + var rootFolderLayout = new RootFolderLayout(); + this.listenToOnce(rootFolderLayout, 'folderSelected', this._setRootFolder); + AppLayout.modalRegion.show(rootFolderLayout); + } else { + Config.setValue(Config.Keys.DefaultRootFolderId, rootFolderValue); + } + }, + + _rootFoldersUpdated : function() { + this._configureTemplateHelpers(); + debugger; + this.render(); + }, + _onRefreshCaptcha : function(event) { var self = this; diff --git a/src/UI/Settings/NetImport/Edit/NetImportEditViewTemplate.hbs b/src/UI/Settings/NetImport/Edit/NetImportEditViewTemplate.hbs index c89c11bae..29b30cc5c 100644 --- a/src/UI/Settings/NetImport/Edit/NetImportEditViewTemplate.hbs +++ b/src/UI/Settings/NetImport/Edit/NetImportEditViewTemplate.hbs @@ -48,6 +48,14 @@ +
+ + +
+ {{> RootFolderSelectionPartial rootFolders}} +
+
+ {{formBuilder}} diff --git a/src/UI/Settings/NetImport/NetImportLayout.js b/src/UI/Settings/NetImport/NetImportLayout.js index 1e4cf6fc8..def13963e 100644 --- a/src/UI/Settings/NetImport/NetImportLayout.js +++ b/src/UI/Settings/NetImport/NetImportLayout.js @@ -2,6 +2,7 @@ var Marionette = require('marionette'); var NetImportCollection = require('./NetImportCollection'); var CollectionView = require('./NetImportCollectionView'); var OptionsView = require('./Options/NetImportOptionsView'); +var RootFolderCollection = require('../../AddMovies/RootFolders/RootFolderCollection'); module.exports = Marionette.Layout.extend({ template : 'Settings/NetImport/NetImportLayoutTemplate', @@ -14,6 +15,9 @@ module.exports = Marionette.Layout.extend({ initialize : function() { this.indexersCollection = new NetImportCollection(); this.indexersCollection.fetch(); + RootFolderCollection.fetch().done(function() { + RootFolderCollection.synced = true; + }); }, onShow : function() {