diff --git a/src/NzbDrone.Api/NzbDrone.Api.csproj b/src/NzbDrone.Api/NzbDrone.Api.csproj index 6a61d84fc..ff526e2bc 100644 --- a/src/NzbDrone.Api/NzbDrone.Api.csproj +++ b/src/NzbDrone.Api/NzbDrone.Api.csproj @@ -118,6 +118,7 @@ + @@ -260,6 +261,7 @@ + diff --git a/src/NzbDrone.Api/Wanted/MissingModule.cs b/src/NzbDrone.Api/Wanted/MissingModule.cs index 9f6215a2e..52470cd1a 100644 --- a/src/NzbDrone.Api/Wanted/MissingModule.cs +++ b/src/NzbDrone.Api/Wanted/MissingModule.cs @@ -12,7 +12,7 @@ namespace NzbDrone.Api.Wanted ISeriesService seriesService, IQualityUpgradableSpecification qualityUpgradableSpecification, IBroadcastSignalRMessage signalRBroadcaster) - : base(episodeService, seriesService, qualityUpgradableSpecification, signalRBroadcaster, "wanted/missing") + : base(episodeService, seriesService, qualityUpgradableSpecification, signalRBroadcaster, "wanted/missing_episodes") { GetResourcePaged = GetMissingEpisodes; } diff --git a/src/NzbDrone.Api/Wanted/MovieMissingModule.cs b/src/NzbDrone.Api/Wanted/MovieMissingModule.cs new file mode 100644 index 000000000..9a46b9818 --- /dev/null +++ b/src/NzbDrone.Api/Wanted/MovieMissingModule.cs @@ -0,0 +1,77 @@ +using NzbDrone.Api.Movie; +using NzbDrone.Api.Movies; +using NzbDrone.Core.DecisionEngine; +using NzbDrone.Core.Tv; +using NzbDrone.Core.Datastore; +using NzbDrone.SignalR; +using NzbDrone.Core.Download; +using NzbDrone.Core.MediaFiles.Events; +using NzbDrone.Core.Messaging.Events; +using System; +using NzbDrone.Core.Datastore.Events; + +namespace NzbDrone.Api.Wanted +{ + class MovieMissingModule : NzbDroneRestModuleWithSignalR, + IHandle, + IHandle + { + protected readonly IMovieService _movieService; + + public MovieMissingModule(IMovieService movieService, + IQualityUpgradableSpecification qualityUpgradableSpecification, + IBroadcastSignalRMessage signalRBroadcaster) + : base(signalRBroadcaster, "wanted/missing") + { + + _movieService = movieService; + GetResourcePaged = GetMissingMovies; + } + + private PagingResource GetMissingMovies(PagingResource pagingResource) + { + var pagingSpec = pagingResource.MapToPagingSpec("physicalRelease", SortDirection.Descending); + + if (pagingResource.FilterKey == "monitored" && pagingResource.FilterValue == "false") + { + pagingSpec.FilterExpression = v => v.Monitored == false; + } + else + { + pagingSpec.FilterExpression = v => v.Monitored == true; + } + + var resource = ApplyToPage(_movieService.MoviesWithoutFiles, pagingSpec, v => MapToResource(v, false)); + + 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(); + return resource; + } + + public void Handle(MovieGrabbedEvent message) + { + var resource = message.Movie.Movie.ToResource(); + + //add a grabbed field in MovieResource? + //resource.Grabbed = true; + + BroadcastResourceChange(ModelAction.Updated, resource); + } + + public void Handle(MovieDownloadedEvent message) + { + BroadcastResourceChange(ModelAction.Updated, message.Movie.Movie.Id); + } + } +} diff --git a/src/NzbDrone.Core/Tv/MovieRepository.cs b/src/NzbDrone.Core/Tv/MovieRepository.cs index f734c5174..d9a8cc32e 100644 --- a/src/NzbDrone.Core/Tv/MovieRepository.cs +++ b/src/NzbDrone.Core/Tv/MovieRepository.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Collections.Generic; using NzbDrone.Core.Datastore; using NzbDrone.Core.Messaging.Events; - +using NzbDrone.Core.Datastore.Extensions; namespace NzbDrone.Core.Tv { @@ -15,6 +15,7 @@ namespace NzbDrone.Core.Tv Movie FindByImdbId(string imdbid); Movie FindByTitleSlug(string slug); List MoviesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored); + PagingSpec MoviesWithoutFiles(PagingSpec pagingSpec); List GetMoviesByFileId(int fileId); void SetFileId(int fileId, int movieId); } @@ -132,5 +133,21 @@ namespace NzbDrone.Core.Tv return query.ToList(); } + + public PagingSpec MoviesWithoutFiles(PagingSpec pagingSpec) + { + + var query = Query.Where(pagingSpec.FilterExpression) + .AndWhere(m => m.MovieFileId == 0) + .AndWhere(m => m.Status == MovieStatusType.Released) + .OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection()) + .Skip(pagingSpec.PagingOffset()) + .Take(pagingSpec.PageSize); + + pagingSpec.Records = query.ToList(); + pagingSpec.TotalRecords = pagingSpec.Records.Count; + + return pagingSpec; + } } } \ No newline at end of file diff --git a/src/NzbDrone.Core/Tv/MovieService.cs b/src/NzbDrone.Core/Tv/MovieService.cs index 0cbf226e4..683182f36 100644 --- a/src/NzbDrone.Core/Tv/MovieService.cs +++ b/src/NzbDrone.Core/Tv/MovieService.cs @@ -12,6 +12,7 @@ using NzbDrone.Core.Parser; using NzbDrone.Core.Tv.Events; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles.Events; +using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Tv { @@ -27,6 +28,7 @@ namespace NzbDrone.Core.Tv Movie FindByTitleSlug(string slug); Movie GetMovieByFileId(int fileId); List GetMoviesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored); + PagingSpec MoviesWithoutFiles(PagingSpec pagingSpec); void DeleteMovie(int movieId, bool deleteFiles); List GetAllMovies(); Movie UpdateMovie(Movie movie); @@ -232,5 +234,12 @@ namespace NzbDrone.Core.Tv return episodes; } + + public PagingSpec MoviesWithoutFiles(PagingSpec pagingSpec) + { + var movieResult = _movieRepository.MoviesWithoutFiles(pagingSpec); + + return movieResult; + } } } diff --git a/src/UI/Cells/MovieStatusWithTextCell.js b/src/UI/Cells/MovieStatusWithTextCell.js new file mode 100644 index 000000000..c4bcac645 --- /dev/null +++ b/src/UI/Cells/MovieStatusWithTextCell.js @@ -0,0 +1,42 @@ +var NzbDroneCell = require('./NzbDroneCell'); + +//used in Wanted tab +module.exports = NzbDroneCell.extend({ + className : 'movie-status-text-cell', + + render : function() { + this.$el.empty(); + var monitored = this.model.get('monitored'); + var status = this.model.get('status'); + var inCinemas = this.model.get("inCinemas"); + var date = new Date(inCinemas); + var timeSince = new Date().getTime() - date.getTime(); + var numOfMonths = timeSince / 1000 / 60 / 60 / 24 / 30; + + if (status === 'released') { + this.$el.html('
 Released
'); + this._setStatusWeight(3); + } + + if (numOfMonths > 3) { + this.$el.html('
 Released
');//TODO: Update for PreDB.me + this._setStatusWeight(2); + } + + if (numOfMonths < 3) { + this.$el.html('
 In Cinemas
'); + this._setStatusWeight(2); + } + + if (status === "announced") { + this.$el.html('
 Announced
'); + this._setStatusWeight(1); + } + + return this; + }, + + _setStatusWeight : function(weight) { + this.model.set('statusWeight', weight, { silent : true }); + } +}); diff --git a/src/UI/Cells/cells.less b/src/UI/Cells/cells.less index ca71defbd..a5a13707a 100644 --- a/src/UI/Cells/cells.less +++ b/src/UI/Cells/cells.less @@ -55,6 +55,10 @@ width : 150px; } +.movie-status-text-cell { + width : 150px; +} + .history-event-type-cell { width : 10px; } diff --git a/src/UI/Wanted/Missing/MissingCollection.js b/src/UI/Wanted/Missing/MissingCollection.js index 28ceee62e..59f91ed67 100644 --- a/src/UI/Wanted/Missing/MissingCollection.js +++ b/src/UI/Wanted/Missing/MissingCollection.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/missing', - model : EpisodeModel, + model : MovieModel, tableName : 'wanted.missing', state : { pageSize : 15, - sortKey : 'airDateUtc', - order : 1 + sortKey : 'inCinemas', + order : -1 }, queryParams : { @@ -39,10 +39,6 @@ var Collection = PagableCollection.extend({ ] }, - sortMappings : { - 'series' : { sortKey : 'series.sortTitle' } - }, - parseState : function(resp) { return { totalRecords : resp.totalRecords }; }, @@ -58,4 +54,4 @@ var Collection = PagableCollection.extend({ Collection = AsFilteredCollection.call(Collection); Collection = AsSortedCollection.call(Collection); -module.exports = AsPersistedStateCollection.call(Collection); \ No newline at end of file +module.exports = AsPersistedStateCollection.call(Collection); diff --git a/src/UI/Wanted/Missing/MissingLayout.js b/src/UI/Wanted/Missing/MissingLayout.js index 96096e780..b3aa9e378 100644 --- a/src/UI/Wanted/Missing/MissingLayout.js +++ b/src/UI/Wanted/Missing/MissingLayout.js @@ -5,11 +5,9 @@ var Marionette = require('marionette'); var Backgrid = require('backgrid'); var MissingCollection = require('./MissingCollection'); 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 RelativeDateCell = require('../../Cells/RelativeDateCell'); -var EpisodeStatusCell = require('../../Cells/EpisodeStatusCell'); +var MovieStatusWithTextCell = require('../../Cells/MovieStatusWithTextCell'); var GridPager = require('../../Shared/Grid/Pager'); var ToolbarLayout = require('../../Shared/Toolbar/ToolbarLayout'); var LoadingView = require('../../Shared/LoadingView'); @@ -39,35 +37,29 @@ module.exports = Marionette.Layout.extend({ headerCell : 'select-all', sortable : false }, - { - name : 'series', - label : 'Series Title', - cell : SeriesTitleCell, - sortValue : 'series.sortTitle' - }, { name : 'this', - label : 'Episode', - cell : EpisodeNumberCell, + label : 'Movie Title', + cell : MovieTitleCell, sortable : false }, { - name : 'this', - label : 'Episode Title', - cell : EpisodeTitleCell, - sortable : false + name : 'inCinemas', + label : 'In Cinemas', + cell : RelativeDateCell }, { - name : 'airDateUtc', - label : 'Air Date', + name : 'physicalRelease', + label : 'PhysicalRelease', cell : RelativeDateCell }, { name : 'status', label : 'Status', - cell : EpisodeStatusCell, + cell : MovieStatusWithTextCell, sortable : false - } + }, + ], initialize : function() {