diff --git a/src/NzbDrone.Api/NzbDrone.Api.csproj b/src/NzbDrone.Api/NzbDrone.Api.csproj
index 0df46ff4a..ec5e30df4 100644
--- a/src/NzbDrone.Api/NzbDrone.Api.csproj
+++ b/src/NzbDrone.Api/NzbDrone.Api.csproj
@@ -267,6 +267,7 @@
+
diff --git a/src/NzbDrone.Api/Wanted/MovieCutoffModule.cs b/src/NzbDrone.Api/Wanted/MovieCutoffModule.cs
new file mode 100644
index 000000000..b454d5d78
--- /dev/null
+++ b/src/NzbDrone.Api/Wanted/MovieCutoffModule.cs
@@ -0,0 +1,48 @@
+using NzbDrone.Api.Movie;
+using NzbDrone.Api.Movies;
+using NzbDrone.Core.DecisionEngine;
+using NzbDrone.Core.Tv;
+using NzbDrone.Core.Datastore;
+using NzbDrone.SignalR;
+
+namespace NzbDrone.Api.Wanted
+{
+ public class MovieCutoffModule : NzbDroneRestModuleWithSignalR
+ {
+ private readonly IMovieCutoffService _movieCutoffService;
+
+ public MovieCutoffModule(IMovieCutoffService movieCutoffService,
+ IMovieService movieService,
+ IQualityUpgradableSpecification qualityUpgradableSpecification,
+ IBroadcastSignalRMessage signalRBroadcaster)
+ : base(signalRBroadcaster, "wanted/cutoff")
+ {
+ _movieCutoffService = movieCutoffService;
+ GetResourcePaged = GetCutoffUnmetMovies;
+ }
+
+ private PagingResource GetCutoffUnmetMovies(PagingResource pagingResource)
+ {
+ var pagingSpec = pagingResource.MapToPagingSpec("title", SortDirection.Ascending);
+
+ if (pagingResource.FilterKey == "monitored" && pagingResource.FilterValue == "false")
+ {
+ pagingSpec.FilterExpression = v => v.Monitored == false;
+ }
+ else
+ {
+ pagingSpec.FilterExpression = v => v.Monitored == true;
+ }
+
+ var resource = ApplyToPage(_movieCutoffService.MoviesWhereCutoffUnmet, pagingSpec, v => MapToResource(v));
+
+ return resource;
+ }
+
+ private MovieResource MapToResource(Core.Tv.Movie movie)
+ {
+ var resource = movie.ToResource();
+ return resource;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/NzbDrone.Api/Wanted/MovieMissingModule.cs b/src/NzbDrone.Api/Wanted/MovieMissingModule.cs
index ea6c63fe1..4b1b89503 100644
--- a/src/NzbDrone.Api/Wanted/MovieMissingModule.cs
+++ b/src/NzbDrone.Api/Wanted/MovieMissingModule.cs
@@ -30,7 +30,7 @@ namespace NzbDrone.Api.Wanted
private PagingResource GetMissingMovies(PagingResource pagingResource)
{
- var pagingSpec = pagingResource.MapToPagingSpec("physicalRelease", SortDirection.Descending);
+ var pagingSpec = pagingResource.MapToPagingSpec("title", SortDirection.Descending);
if (pagingResource.FilterKey == "monitored" && pagingResource.FilterValue == "false")
{
@@ -46,13 +46,6 @@ namespace NzbDrone.Api.Wanted
return resource;
}
- private MovieResource GetMovie(int id)
- {
- var movie = _movieService.GetMovie(id);
- var resource = MapToResource(movie, true);
- return resource;
- }
-
private MovieResource MapToResource(Core.Tv.Movie movie, bool includeMovieFile)
{
var resource = movie.ToResource();
diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj
index 644461930..37bfae84f 100644
--- a/src/NzbDrone.Core/NzbDrone.Core.csproj
+++ b/src/NzbDrone.Core/NzbDrone.Core.csproj
@@ -1164,6 +1164,7 @@
+
diff --git a/src/NzbDrone.Core/Tv/MovieCutoffService.cs b/src/NzbDrone.Core/Tv/MovieCutoffService.cs
new file mode 100644
index 000000000..7d55257cd
--- /dev/null
+++ b/src/NzbDrone.Core/Tv/MovieCutoffService.cs
@@ -0,0 +1,48 @@
+using System.Collections.Generic;
+using System.Linq;
+using NLog;
+using NzbDrone.Core.Datastore;
+using NzbDrone.Core.Profiles;
+using NzbDrone.Core.Qualities;
+
+namespace NzbDrone.Core.Tv
+{
+ public interface IMovieCutoffService
+ {
+ PagingSpec MoviesWhereCutoffUnmet(PagingSpec pagingSpec);
+ }
+
+ public class MovieCutoffService : IMovieCutoffService
+ {
+ private readonly IMovieRepository _movieRepository;
+ private readonly IProfileService _profileService;
+ private readonly Logger _logger;
+
+ public MovieCutoffService(IMovieRepository movieRepository, IProfileService profileService, Logger logger)
+ {
+ _movieRepository = movieRepository;
+ _profileService = profileService;
+ _logger = logger;
+ }
+
+ public PagingSpec MoviesWhereCutoffUnmet(PagingSpec pagingSpec)
+ {
+ var qualitiesBelowCutoff = new List();
+ var profiles = _profileService.All();
+
+ //Get all items less than the cutoff
+ foreach (var profile in profiles)
+ {
+ var cutoffIndex = profile.Items.FindIndex(v => v.Quality == profile.Cutoff);
+ var belowCutoff = profile.Items.Take(cutoffIndex).ToList();
+
+ if (belowCutoff.Any())
+ {
+ qualitiesBelowCutoff.Add(new QualitiesBelowCutoff(profile.Id, belowCutoff.Select(i => i.Quality.Id)));
+ }
+ }
+
+ return _movieRepository.MoviesWhereCutoffUnmet(pagingSpec, qualitiesBelowCutoff);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/NzbDrone.Core/Tv/MovieRepository.cs b/src/NzbDrone.Core/Tv/MovieRepository.cs
index e9cd70af7..974fdc3d3 100644
--- a/src/NzbDrone.Core/Tv/MovieRepository.cs
+++ b/src/NzbDrone.Core/Tv/MovieRepository.cs
@@ -6,6 +6,7 @@ using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Datastore.Extensions;
using Marr.Data.QGen;
using NzbDrone.Core.MediaFiles;
+using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.Tv
{
@@ -22,6 +23,7 @@ namespace NzbDrone.Core.Tv
PagingSpec MoviesWithoutFiles(PagingSpec pagingSpec);
List GetMoviesByFileId(int fileId);
void SetFileId(int fileId, int movieId);
+ PagingSpec MoviesWhereCutoffUnmet(PagingSpec pagingSpec, List qualitiesBelowCutoff);
}
public class MovieRepository : BasicRepository, IMovieRepository
@@ -200,6 +202,41 @@ namespace NzbDrone.Core.Tv
.Take(pagingSpec.PageSize);
}
+ public PagingSpec MoviesWhereCutoffUnmet(PagingSpec pagingSpec, List qualitiesBelowCutoff)
+ {
+
+ pagingSpec.TotalRecords = MoviesWhereCutoffUnmetQuery(pagingSpec, qualitiesBelowCutoff).GetRowCount();
+ pagingSpec.Records = MoviesWhereCutoffUnmetQuery(pagingSpec, qualitiesBelowCutoff).ToList();
+
+ return pagingSpec;
+ }
+
+ private SortBuilder MoviesWhereCutoffUnmetQuery(PagingSpec pagingSpec, List qualitiesBelowCutoff)
+ {
+ return Query.Join(JoinType.Left, e => e.MovieFile, (e, s) => e.MovieFileId == s.Id)
+ .Where(pagingSpec.FilterExpression)
+ .AndWhere(m => m.MovieFileId != 0)
+ .AndWhere(BuildQualityCutoffWhereClause(qualitiesBelowCutoff))
+ .OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection())
+ .Skip(pagingSpec.PagingOffset())
+ .Take(pagingSpec.PageSize);
+ }
+
+ private string BuildQualityCutoffWhereClause(List qualitiesBelowCutoff)
+ {
+ var clauses = new List();
+
+ foreach (var profile in qualitiesBelowCutoff)
+ {
+ foreach (var belowCutoff in profile.QualityIds)
+ {
+ clauses.Add(string.Format("([t0].[ProfileId] = {0} AND [t1].[Quality] LIKE '%_quality_: {1},%')", profile.ProfileId, belowCutoff));
+ }
+ }
+
+ return string.Format("({0})", string.Join(" OR ", clauses));
+ }
+
public Movie FindByTmdbId(int tmdbid)
{
return Query.Where(m => m.TmdbId == tmdbid).FirstOrDefault();
diff --git a/src/UI/Wanted/Cutoff/CutoffUnmetCollection.js b/src/UI/Wanted/Cutoff/CutoffUnmetCollection.js
index 5f2a6546f..e12a4084c 100644
--- a/src/UI/Wanted/Cutoff/CutoffUnmetCollection.js
+++ b/src/UI/Wanted/Cutoff/CutoffUnmetCollection.js
@@ -1,5 +1,5 @@
var _ = require('underscore');
-var EpisodeModel = require('../../Series/EpisodeModel');
+var MovieModel = require('../../Movies/MovieModel');
var PagableCollection = require('backbone.pageable');
var AsFilteredCollection = require('../../Mixins/AsFilteredCollection');
var AsSortedCollection = require('../../Mixins/AsSortedCollection');
@@ -7,13 +7,13 @@ var AsPersistedStateCollection = require('../../Mixins/AsPersistedStateCollectio
var Collection = PagableCollection.extend({
url : window.NzbDrone.ApiRoot + '/wanted/cutoff',
- model : EpisodeModel,
+ model : MovieModel,
tableName : 'wanted.cutoff',
state : {
pageSize : 15,
- sortKey : 'airDateUtc',
- order : 1
+ sortKey : 'title',
+ order : -1
},
queryParams : {
@@ -40,9 +40,9 @@ var Collection = PagableCollection.extend({
],
},
- sortMappings : {
- 'series' : { sortKey : 'series.sortTitle' }
- },
+ // sortMappings : {
+ // 'this' : { sortKey : 'this.sortTitle' }
+ // },
parseState : function(resp) {
return { totalRecords : resp.totalRecords };
diff --git a/src/UI/Wanted/Cutoff/CutoffUnmetLayout.js b/src/UI/Wanted/Cutoff/CutoffUnmetLayout.js
index 2221f04fe..ef6b9eacc 100644
--- a/src/UI/Wanted/Cutoff/CutoffUnmetLayout.js
+++ b/src/UI/Wanted/Cutoff/CutoffUnmetLayout.js
@@ -3,16 +3,15 @@ var Marionette = require('marionette');
var Backgrid = require('backgrid');
var CutoffUnmetCollection = require('./CutoffUnmetCollection');
var SelectAllCell = require('../../Cells/SelectAllCell');
-var SeriesTitleCell = require('../../Cells/SeriesTitleCell');
-var EpisodeNumberCell = require('../../Cells/EpisodeNumberCell');
-var EpisodeTitleCell = require('../../Cells/EpisodeTitleCell');
+var MovieTitleCell = require('../../Cells/MovieTitleCell');
+var MovieStatusWithTextCell = require('../../Cells/MovieStatusWithTextCell');
var RelativeDateCell = require('../../Cells/RelativeDateCell');
-var EpisodeStatusCell = require('../../Cells/EpisodeStatusCell');
var GridPager = require('../../Shared/Grid/Pager');
var ToolbarLayout = require('../../Shared/Toolbar/ToolbarLayout');
var LoadingView = require('../../Shared/LoadingView');
var Messenger = require('../../Shared/Messenger');
var CommandController = require('../../Commands/CommandController');
+
require('backgrid.selectall');
require('../../Mixins/backbone.signalr.mixin');
@@ -37,32 +36,25 @@ module.exports = Marionette.Layout.extend({
sortable : false
},
{
- name : 'series',
- label : 'Series Title',
- cell : SeriesTitleCell,
- sortValue : 'series.sortTitle'
+ name : 'this',
+ label : 'Movie Title',
+ cell : MovieTitleCell,
+ sortValue : this.sortTitle
},
{
- name : 'this',
- label : 'Episode',
- cell : EpisodeNumberCell,
- sortable : false
+ name : 'inCinemas',
+ label : 'In Cinemas',
+ cell : RelativeDateCell
},
{
- name : 'this',
- label : 'Episode Title',
- cell : EpisodeTitleCell,
- sortable : false
- },
- {
- name : 'airDateUtc',
- label : 'Air Date',
- cell : RelativeDateCell
+ name : 'physicalRelease',
+ label : 'Physical Release',
+ cell : RelativeDateCell
},
{
name : 'status',
label : 'Status',
- cell : EpisodeStatusCell,
+ cell : MovieStatusWithTextCell,
sortable : false
}
],
@@ -105,11 +97,6 @@ module.exports = Marionette.Layout.extend({
callback : this._searchSelected,
ownerContext : this,
className : 'x-search-selected'
- },
- {
- title : 'Season Pass',
- icon : 'icon-sonarr-monitored',
- route : 'seasonpass'
}
]
};
@@ -148,9 +135,9 @@ module.exports = Marionette.Layout.extend({
}));
CommandController.bindToCommand({
- element : this.$('.x-search-selected'),
- command : {
- name : 'episodeSearch'
+ element : this.$('.x-search-selected'),
+ command : {
+ name : 'moviesSearch'
}
});
},
@@ -172,7 +159,7 @@ module.exports = Marionette.Layout.extend({
if (selected.length === 0) {
Messenger.show({
type : 'error',
- message : 'No episodes selected'
+ message : 'No movies selected'
});
return;
@@ -180,9 +167,9 @@ module.exports = Marionette.Layout.extend({
var ids = _.pluck(selected, 'id');
- CommandController.Execute('episodeSearch', {
- name : 'episodeSearch',
- episodeIds : ids
+ CommandController.Execute('moviesSearch', {
+ name : 'moviesSearch',
+ movieIds : ids
});
}
});
\ No newline at end of file
diff --git a/src/UI/Wanted/Missing/MissingLayout.js b/src/UI/Wanted/Missing/MissingLayout.js
index 1572708b0..fbf1206f8 100644
--- a/src/UI/Wanted/Missing/MissingLayout.js
+++ b/src/UI/Wanted/Missing/MissingLayout.js
@@ -49,7 +49,7 @@ module.exports = Marionette.Layout.extend({
},
{
name : 'physicalRelease',
- label : 'PhysicalRelease',
+ label : 'Physical Release',
cell : RelativeDateCell
},
{
@@ -115,11 +115,6 @@ module.exports = Marionette.Layout.extend({
ownerContext : this,
className : 'x-unmonitor-selected'
},
- {
- title : 'Season Pass',
- icon : 'icon-sonarr-monitored',
- route : 'seasonpass'
- },
{
title : 'Rescan Drone Factory Folder',
icon : 'icon-sonarr-refresh',