diff --git a/src/NzbDrone.Api/Profiles/ProfileResource.cs b/src/NzbDrone.Api/Profiles/ProfileResource.cs index ee02bcb32..65e560b59 100644 --- a/src/NzbDrone.Api/Profiles/ProfileResource.cs +++ b/src/NzbDrone.Api/Profiles/ProfileResource.cs @@ -11,6 +11,7 @@ namespace NzbDrone.Api.Profiles { public string Name { get; set; } public Quality Cutoff { get; set; } + public string PreferredTags { get; set; } public List Items { get; set; } public Language Language { get; set; } } @@ -33,6 +34,7 @@ namespace NzbDrone.Api.Profiles Name = model.Name, Cutoff = model.Cutoff, + PreferredTags = model.PreferredTags != null ? string.Join(",", model.PreferredTags) : "", Items = model.Items.ConvertAll(ToResource), Language = model.Language }; @@ -59,6 +61,7 @@ namespace NzbDrone.Api.Profiles Name = resource.Name, Cutoff = (Quality)resource.Cutoff.Id, + PreferredTags = resource.PreferredTags.Split(',').ToList(), Items = resource.Items.ConvertAll(ToModel), Language = resource.Language }; diff --git a/src/NzbDrone.Core/Datastore/Migration/124_add_preferred_tags_to_profile.cs b/src/NzbDrone.Core/Datastore/Migration/124_add_preferred_tags_to_profile.cs new file mode 100644 index 000000000..531af0eb6 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/124_add_preferred_tags_to_profile.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; +using System.Data; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(124)] + public class add_preferred_tags_to_profile : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Alter.Table("Profiles").AddColumn("PreferredTags").AsString().Nullable(); + } + + } +} diff --git a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs index aba427cbf..fdb45f6d4 100644 --- a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs +++ b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs @@ -23,6 +23,7 @@ namespace NzbDrone.Core.DecisionEngine var comparers = new List { CompareQuality, + ComparePreferredWords, CompareProtocol, ComparePeersIfTorrent, CompareAgeIfUsenet, @@ -65,6 +66,26 @@ namespace NzbDrone.Core.DecisionEngine CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Version)); } + private int ComparePreferredWords(DownloadDecision x, DownloadDecision y) + { + return CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => + { + var title = remoteMovie.Release.Title; + remoteMovie.Movie.Profile.LazyLoad(); + var preferredWords = remoteMovie.Movie.Profile.Value.PreferredTags; + + if (preferredWords == null) + { + return 0; + } + + var num = preferredWords.AsEnumerable().Count(w => title.ToLower().Contains(w.ToLower())); + + return num; + + }); +; } + private int CompareProtocol(DownloadDecision x, DownloadDecision y) { diff --git a/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs b/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs index d89f3aa42..8ff95a672 100644 --- a/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs +++ b/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs @@ -56,6 +56,7 @@ namespace NzbDrone.Core.Notifications.CustomScript environmentVariables.Add("Radarr_EventType", "Download"); environmentVariables.Add("Radarr_Movie_Id", movie.Id.ToString()); environmentVariables.Add("Radarr_Movie_Title", movie.Title); + environmentVariables.Add("Radarr_Movie_Path", movie.Path); environmentVariables.Add("Radarr_Movie_ImdbId", movie.ImdbId.ToString()); environmentVariables.Add("Radarr_MovieFile_Id", movieFile.Id.ToString()); environmentVariables.Add("Radarr_MovieFile_RelativePath", movieFile.RelativePath); diff --git a/src/NzbDrone.Core/Notifications/MediaBrowser/MediaBrowser.cs b/src/NzbDrone.Core/Notifications/MediaBrowser/MediaBrowser.cs index b47385736..f293a6ecd 100644 --- a/src/NzbDrone.Core/Notifications/MediaBrowser/MediaBrowser.cs +++ b/src/NzbDrone.Core/Notifications/MediaBrowser/MediaBrowser.cs @@ -37,14 +37,18 @@ namespace NzbDrone.Core.Notifications.MediaBrowser if (Settings.UpdateLibrary) { - _mediaBrowserService.Update(Settings, message.Series); + _mediaBrowserService.UpdateMovies(Settings, message.Movie); } } public override void OnMovieRename(Movie movie) { + if (Settings.UpdateLibrary) + { + _mediaBrowserService.UpdateMovies(Settings, movie); + } } - + public override void OnRename(Series series) { if (Settings.UpdateLibrary) diff --git a/src/NzbDrone.Core/Notifications/MediaBrowser/MediaBrowserProxy.cs b/src/NzbDrone.Core/Notifications/MediaBrowser/MediaBrowserProxy.cs index 251488d87..dafccb99a 100644 --- a/src/NzbDrone.Core/Notifications/MediaBrowser/MediaBrowserProxy.cs +++ b/src/NzbDrone.Core/Notifications/MediaBrowser/MediaBrowserProxy.cs @@ -40,6 +40,16 @@ namespace NzbDrone.Core.Notifications.MediaBrowser ProcessRequest(request, settings); } + + public void UpdateMovies(MediaBrowserSettings settings, string imdbid) + { + var path = string.Format("/Library/Movies/Updated?ImdbId={0}", imdbid); + var request = BuildRequest(path, settings); + request.Headers.Add("Content-Length", "0"); + + ProcessRequest(request, settings); + } + private string ProcessRequest(HttpRequest request, MediaBrowserSettings settings) { request.Headers.Add("X-MediaBrowser-Token", settings.ApiKey); diff --git a/src/NzbDrone.Core/Notifications/MediaBrowser/MediaBrowserService.cs b/src/NzbDrone.Core/Notifications/MediaBrowser/MediaBrowserService.cs index 748d2a67f..9c76145cd 100644 --- a/src/NzbDrone.Core/Notifications/MediaBrowser/MediaBrowserService.cs +++ b/src/NzbDrone.Core/Notifications/MediaBrowser/MediaBrowserService.cs @@ -11,6 +11,7 @@ namespace NzbDrone.Core.Notifications.MediaBrowser { void Notify(MediaBrowserSettings settings, string title, string message); void Update(MediaBrowserSettings settings, Series series); + void UpdateMovies(MediaBrowserSettings settings, Movie movie); ValidationFailure Test(MediaBrowserSettings settings); } @@ -35,6 +36,13 @@ namespace NzbDrone.Core.Notifications.MediaBrowser _proxy.Update(settings, series.TvdbId); } + + public void UpdateMovies(MediaBrowserSettings settings, Movie movie) + { + _proxy.UpdateMovies(settings, movie.ImdbId); + } + + public ValidationFailure Test(MediaBrowserSettings settings) { try diff --git a/src/NzbDrone.Core/Notifications/Xbmc/HttpApiProvider.cs b/src/NzbDrone.Core/Notifications/Xbmc/HttpApiProvider.cs index 76f2bc91f..528728cdf 100644 --- a/src/NzbDrone.Core/Notifications/Xbmc/HttpApiProvider.cs +++ b/src/NzbDrone.Core/Notifications/Xbmc/HttpApiProvider.cs @@ -51,6 +51,24 @@ namespace NzbDrone.Core.Notifications.Xbmc UpdateLibrary(settings, series); } + public void UpdateMovie(XbmcSettings settings, Movie movie) + { + if (!settings.AlwaysUpdate) + { + _logger.Debug("Determining if there are any active players on XBMC host: {0}", settings.Address); + var activePlayers = GetActivePlayers(settings); + + if (activePlayers.Any(a => a.Type.Equals("video"))) + { + _logger.Debug("Video is currently playing, skipping library update"); + return; + } + } + + UpdateMovieLibrary(settings, movie); + } + + public void Clean(XbmcSettings settings) { const string cleanVideoLibrary = "CleanLibrary(video)"; @@ -167,6 +185,37 @@ namespace NzbDrone.Core.Notifications.Xbmc } } + private void UpdateMovieLibrary(XbmcSettings settings, Movie movie) + { + try + { + //_logger.Debug("Sending Update DB Request to XBMC Host: {0}", settings.Address); + //var xbmcSeriesPath = GetSeriesPath(settings, series); + + ////If the path is found update it, else update the whole library + //if (!string.IsNullOrEmpty(xbmcSeriesPath)) + //{ + // _logger.Debug("Updating series [{0}] on XBMC host: {1}", series, settings.Address); + // var command = BuildExecBuiltInCommand(string.Format("UpdateLibrary(video,{0})", xbmcSeriesPath)); + // SendCommand(settings, command); + //} + + //else + //{ + //Update the entire library + _logger.Debug("Series [{0}] doesn't exist on XBMC host: {1}, Updating Entire Library", movie, settings.Address); + var command = BuildExecBuiltInCommand("UpdateLibrary(video)"); + SendCommand(settings, command); + //} + } + + catch (Exception ex) + { + _logger.Debug(ex, ex.Message); + } + } + + private string SendCommand(XbmcSettings settings, string command) { var url = string.Format("http://{0}/xbmcCmds/xbmcHttp?command={1}", settings.Address, command); diff --git a/src/NzbDrone.Core/Notifications/Xbmc/IApiProvider.cs b/src/NzbDrone.Core/Notifications/Xbmc/IApiProvider.cs index bf250edc3..94bf80862 100644 --- a/src/NzbDrone.Core/Notifications/Xbmc/IApiProvider.cs +++ b/src/NzbDrone.Core/Notifications/Xbmc/IApiProvider.cs @@ -7,6 +7,7 @@ namespace NzbDrone.Core.Notifications.Xbmc { void Notify(XbmcSettings settings, string title, string message); void Update(XbmcSettings settings, Series series); + void UpdateMovie(XbmcSettings settings, Movie movie); void Clean(XbmcSettings settings); bool CanHandle(XbmcVersion version); } diff --git a/src/NzbDrone.Core/Notifications/Xbmc/JsonApiProvider.cs b/src/NzbDrone.Core/Notifications/Xbmc/JsonApiProvider.cs index 1a0674908..378bb0774 100644 --- a/src/NzbDrone.Core/Notifications/Xbmc/JsonApiProvider.cs +++ b/src/NzbDrone.Core/Notifications/Xbmc/JsonApiProvider.cs @@ -44,7 +44,25 @@ namespace NzbDrone.Core.Notifications.Xbmc UpdateLibrary(settings, series); } - + + public void UpdateMovie(XbmcSettings settings, Movie movie) + { + if (!settings.AlwaysUpdate) + { + _logger.Debug("Determining if there are any active players on XBMC host: {0}", settings.Address); + var activePlayers = _proxy.GetActivePlayers(settings); + + if (activePlayers.Any(a => a.Type.Equals("video"))) + { + _logger.Debug("Video is currently playing, skipping library update"); + return; + } + } + + UpdateMovieLibrary(settings, movie); + } + + public void Clean(XbmcSettings settings) { _proxy.CleanLibrary(settings); @@ -108,5 +126,23 @@ namespace NzbDrone.Core.Notifications.Xbmc _logger.Debug(ex, ex.Message); } } + + private void UpdateMovieLibrary(XbmcSettings settings, Movie movie) + { + try + { + var response = _proxy.UpdateLibrary(settings, null); + + if (!response.Equals("OK", StringComparison.InvariantCultureIgnoreCase)) + { + _logger.Debug("Failed to update library for: {0}", settings.Address); + } + } + + catch (Exception ex) + { + _logger.Debug(ex, ex.Message); + } + } } } diff --git a/src/NzbDrone.Core/Notifications/Xbmc/Xbmc.cs b/src/NzbDrone.Core/Notifications/Xbmc/Xbmc.cs index 4939fbe3d..890e0516d 100644 --- a/src/NzbDrone.Core/Notifications/Xbmc/Xbmc.cs +++ b/src/NzbDrone.Core/Notifications/Xbmc/Xbmc.cs @@ -33,13 +33,14 @@ namespace NzbDrone.Core.Notifications.Xbmc const string header = "Radarr - Downloaded"; Notify(Settings, header, message.Message); - UpdateAndClean(message.Series, message.OldFiles.Any()); + UpdateAndCleanMovie(message.Movie, message.OldMovieFiles.Any()); } public override void OnMovieRename(Movie movie) { + UpdateAndCleanMovie(movie); } - + public override void OnRename(Series series) { UpdateAndClean(series); @@ -92,5 +93,26 @@ namespace NzbDrone.Core.Notifications.Xbmc _logger.Debug(ex, logMessage); } } + + private void UpdateAndCleanMovie(Movie movie, bool clean = true) + { + try + { + if (Settings.UpdateLibrary) + { + _xbmcService.UpdateMovie(Settings, movie); + } + + if (clean && Settings.CleanLibrary) + { + _xbmcService.Clean(Settings); + } + } + catch (SocketException ex) + { + var logMessage = string.Format("Unable to connect to XBMC Host: {0}:{1}", Settings.Host, Settings.Port); + _logger.Debug(ex, logMessage); + } + } } } diff --git a/src/NzbDrone.Core/Notifications/Xbmc/XbmcService.cs b/src/NzbDrone.Core/Notifications/Xbmc/XbmcService.cs index 84127f69f..85dbc99c5 100644 --- a/src/NzbDrone.Core/Notifications/Xbmc/XbmcService.cs +++ b/src/NzbDrone.Core/Notifications/Xbmc/XbmcService.cs @@ -15,6 +15,7 @@ namespace NzbDrone.Core.Notifications.Xbmc { void Notify(XbmcSettings settings, string title, string message); void Update(XbmcSettings settings, Series series); + void UpdateMovie(XbmcSettings settings, Movie movie); void Clean(XbmcSettings settings); ValidationFailure Test(XbmcSettings settings, string message); } @@ -51,6 +52,12 @@ namespace NzbDrone.Core.Notifications.Xbmc provider.Update(settings, series); } + public void UpdateMovie(XbmcSettings settings, Movie movie) + { + var provider = GetApiProvider(settings); + provider.UpdateMovie(settings, movie); + } + public void Clean(XbmcSettings settings) { var provider = GetApiProvider(settings); diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 6f7eee5c0..d84d0401b 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -183,6 +183,7 @@ + diff --git a/src/NzbDrone.Core/Profiles/Profile.cs b/src/NzbDrone.Core/Profiles/Profile.cs index 6215e9474..d25104fb6 100644 --- a/src/NzbDrone.Core/Profiles/Profile.cs +++ b/src/NzbDrone.Core/Profiles/Profile.cs @@ -11,6 +11,7 @@ namespace NzbDrone.Core.Profiles public string Name { get; set; } public Quality Cutoff { get; set; } public List Items { get; set; } + public List PreferredTags { get; set; } public Language Language { get; set; } public Quality LastAllowedQuality() diff --git a/src/UI/Settings/Profile/Edit/EditProfileView.js b/src/UI/Settings/Profile/Edit/EditProfileView.js index 23535d9e6..52459c6c1 100644 --- a/src/UI/Settings/Profile/Edit/EditProfileView.js +++ b/src/UI/Settings/Profile/Edit/EditProfileView.js @@ -4,25 +4,37 @@ var LanguageCollection = require('../Language/LanguageCollection'); var Config = require('../../../Config'); var AsModelBoundView = require('../../../Mixins/AsModelBoundView'); var AsValidatedView = require('../../../Mixins/AsValidatedView'); +require('../../../Mixins/TagInput'); +require('bootstrap'); +require('bootstrap.tagsinput'); var view = Marionette.ItemView.extend({ - template : 'Settings/Profile/Edit/EditProfileViewTemplate', + template : 'Settings/Profile/Edit/EditProfileViewTemplate', - ui : { cutoff : '.x-cutoff' }, + ui : { cutoff : '.x-cutoff', + preferred : '.x-preferred', + }, - templateHelpers : function() { - return { - languages : LanguageCollection.toJSON() - }; - }, + onRender : function() { + this.ui.preferred.tagsinput({ + trimValue : true, + tagClass : 'label label-success' + }); + }, - getCutoff : function() { - var self = this; + templateHelpers : function() { + return { + languages : LanguageCollection.toJSON() + }; + }, - return _.findWhere(_.pluck(this.model.get('items'), 'quality'), { id : parseInt(self.ui.cutoff.val(), 10) }); - } + getCutoff : function() { + var self = this; + + return _.findWhere(_.pluck(this.model.get('items'), 'quality'), { id : parseInt(self.ui.cutoff.val(), 10) }); + } }); AsValidatedView.call(view); -module.exports = AsModelBoundView.call(view); \ No newline at end of file +module.exports = AsModelBoundView.call(view); diff --git a/src/UI/Settings/Profile/Edit/EditProfileViewTemplate.hbs b/src/UI/Settings/Profile/Edit/EditProfileViewTemplate.hbs index c19d10e5c..072a70ed0 100644 --- a/src/UI/Settings/Profile/Edit/EditProfileViewTemplate.hbs +++ b/src/UI/Settings/Profile/Edit/EditProfileViewTemplate.hbs @@ -1,45 +1,59 @@
- + -
- -
+
+ +

- - -
- -
- -
- -
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+
+ +
- - -
- -
- -
- -
+ + +
+ +
+ +
+ +
diff --git a/src/UI/Settings/Profile/ProfileView.js b/src/UI/Settings/Profile/ProfileView.js index 4241c3f12..10a4a9be3 100644 --- a/src/UI/Settings/Profile/ProfileView.js +++ b/src/UI/Settings/Profile/ProfileView.js @@ -6,30 +6,32 @@ require('./AllowedLabeler'); require('./LanguageLabel'); require('bootstrap'); + var view = Marionette.ItemView.extend({ - template : 'Settings/Profile/ProfileViewTemplate', - tagName : 'li', - - ui : { - "progressbar" : '.progress .bar', - "deleteButton" : '.x-delete' - }, - - events : { - 'click' : '_editProfile' - }, - - initialize : function() { - this.listenTo(this.model, 'sync', this.render); - }, - - _editProfile : function() { - var view = new EditProfileView({ - model : this.model, - profileCollection : this.model.collection - }); - AppLayout.modalRegion.show(view); - } + template : 'Settings/Profile/ProfileViewTemplate', + tagName : 'li', + + ui : { + "progressbar" : '.progress .bar', + "deleteButton" : '.x-delete', + + }, + + events : { + 'click' : '_editProfile' + }, + + initialize : function() { + this.listenTo(this.model, 'sync', this.render); + }, + + _editProfile : function() { + var view = new EditProfileView({ + model : this.model, + profileCollection : this.model.collection + }); + AppLayout.modalRegion.show(view); + } }); -module.exports = AsModelBoundView.call(view); \ No newline at end of file +module.exports = AsModelBoundView.call(view); diff --git a/src/UI/Settings/Profile/ProfileViewTemplate.hbs b/src/UI/Settings/Profile/ProfileViewTemplate.hbs index 4f5b3eef0..2f827a351 100644 --- a/src/UI/Settings/Profile/ProfileViewTemplate.hbs +++ b/src/UI/Settings/Profile/ProfileViewTemplate.hbs @@ -1,13 +1,13 @@
-
-

-
+
+

+
-
- {{languageLabel}} -
+
+ {{languageLabel}} +
-
    - {{allowedLabeler}} -
-
\ No newline at end of file +
    + {{allowedLabeler}} +
+