Paging for movies :) (#861)

* First steps.

* Not really sure what I am doing here.

* Pretty hacky, but it works :)

* First filter works now.

* Fix all filters.

* Fix some filters.

* PageSize saving now works.

* Fixed items being added when a refresh movie is done.

* Downloaded sort not working.

* Sorting by downloaded status now works.

Extremely hacky, but ¯\_(ツ)_/¯

* Fixed issue where users were stuck when filtering.

* Sorting via that button works now.

* Removed temp thingy.
pull/2/head
Leonardo Galli 8 years ago committed by GitHub
parent 50fdbd896c
commit f07f2e77f6

@ -1,4 +1,6 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Api
@ -38,5 +40,14 @@ namespace NzbDrone.Api
return pagingSpec;
}
/*public static Expression<Func<TModel, object>> CreateFilterExpression<TModel>(string filterKey, string filterValue)
{
Type type = typeof(TModel);
ParameterExpression parameterExpression = Expression.Parameter(type, "x");
Expression expressionBody = parameterExpression;
return expressionBody;
}*/
}
}

@ -15,6 +15,7 @@ using NzbDrone.Core.Validation.Paths;
using NzbDrone.Core.DataAugmentation.Scene;
using NzbDrone.Core.Validation;
using NzbDrone.SignalR;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Api.Movie
{
@ -52,6 +53,7 @@ namespace NzbDrone.Api.Movie
_coverMapper = coverMapper;
GetResourceAll = AllMovie;
GetResourcePaged = GetMoviePaged;
GetResourceById = GetMovie;
CreateResource = AddMovie;
UpdateResource = UpdateMovie;
@ -104,6 +106,43 @@ namespace NzbDrone.Api.Movie
return MapToResource(movies);
}
private PagingResource<MovieResource> GetMoviePaged(PagingResource<MovieResource> pagingResource)
{
var pagingSpec = pagingResource.MapToPagingSpec<MovieResource, Core.Tv.Movie>();
if (pagingResource.FilterKey == "monitored" && pagingResource.FilterValue == "false")
{
pagingSpec.FilterExpression = v => v.Monitored == false;
}
else if (pagingResource.FilterKey == "monitored")
{
pagingSpec.FilterExpression = v => v.Monitored == true;
}
if (pagingResource.FilterKey == "status")
{
switch (pagingResource.FilterValue)
{
case "released":
pagingSpec.FilterExpression = v => v.Status == MovieStatusType.Released;
break;
case "inCinemas":
pagingSpec.FilterExpression = v => v.Status == MovieStatusType.InCinemas;
break;
case "announced":
pagingSpec.FilterExpression = v => v.Status == MovieStatusType.Announced;
break;
}
}
if (pagingResource.FilterKey == "downloaded")
{
pagingSpec.FilterExpression = v => v.MovieFileId != 0;
}
return ApplyToPage(_moviesService.Paged, pagingSpec, MovieResourceMapper.ToResource);
}
protected MovieResource MapToResource(Core.Tv.Movie movies)
{
if (movies == null) return null;

@ -43,9 +43,12 @@ namespace NzbDrone.Core.Tv
}; //If a movie has more than 10 parts fuck 'em.
protected IMainDatabase _database;
public MovieRepository(IMainDatabase database, IEventAggregator eventAggregator)
: base(database, eventAggregator)
{
_database = database;
}
public bool MoviePathExists(string path)
@ -192,6 +195,42 @@ namespace NzbDrone.Core.Tv
return pagingSpec;
}
public override PagingSpec<Movie> GetPaged(PagingSpec<Movie> pagingSpec)
{
if (pagingSpec.SortKey == "downloadedQuality")
{
var mapper = _database.GetDataMapper();
var offset = pagingSpec.PagingOffset();
var limit = pagingSpec.PageSize;
var direction = "ASC";
if (pagingSpec.SortDirection == NzbDrone.Core.Datastore.SortDirection.Descending)
{
direction = "DESC";
}
var q = mapper.Query<Movie>($"SELECT * from \"Movies\" , \"MovieFiles\", \"QualityDefinitions\" WHERE Movies.MovieFileId=MovieFiles.Id AND instr(MovieFiles.Quality, ('quality\": ' || QualityDefinitions.Quality || \",\")) > 0 ORDER BY QualityDefinitions.Title {direction} LIMIT {offset},{limit};");
var q2 = mapper.Query<Movie>("SELECT * from \"Movies\" , \"MovieFiles\", \"QualityDefinitions\" WHERE Movies.MovieFileId=MovieFiles.Id AND instr(MovieFiles.Quality, ('quality\": ' || QualityDefinitions.Quality || \",\")) > 0 ORDER BY QualityDefinitions.Title ASC;");
//var ok = q.BuildQuery();
pagingSpec.Records = q.ToList();
pagingSpec.TotalRecords = q2.Count();
}
else
{
pagingSpec = base.GetPaged(pagingSpec);
}
if (pagingSpec.Records.Count == 0 && pagingSpec.PageSize != 1)
{
var lastPossiblePage = pagingSpec.TotalRecords / pagingSpec.PageSize + 1;
pagingSpec.Page = lastPossiblePage;
return GetPaged(pagingSpec);
}
return pagingSpec;
}
public SortBuilder<Movie> GetMoviesWithoutFilesQuery(PagingSpec<Movie> pagingSpec)
{
return Query.Where(pagingSpec.FilterExpression)

@ -20,6 +20,7 @@ namespace NzbDrone.Core.Tv
{
Movie GetMovie(int movieId);
List<Movie> GetMovies(IEnumerable<int> movieIds);
PagingSpec<Movie> Paged(PagingSpec<Movie> pagingSpec);
Movie AddMovie(Movie newMovie);
List<Movie> AddMovies(List<Movie> newMovies);
Movie FindByImdbId(string imdbid);
@ -111,6 +112,11 @@ namespace NzbDrone.Core.Tv
return _movieRepository.Get(movieIds).ToList();
}
public PagingSpec<Movie> Paged(PagingSpec<Movie> pagingSpec)
{
return _movieRepository.GetPaged(pagingSpec);
}
public Movie AddMovie(Movie newMovie)
{
Ensure.That(newMovie, () => newMovie).IsNotNull();

@ -110,7 +110,7 @@ namespace NzbDrone.Core.Tv
if (message.MovieId.HasValue)
{
var movie = _movieService.GetMovie(message.MovieId.Value);
RefreshMovieInfo(movie);
RefreshMovieInfo(movie);
}
else
{

@ -2404,7 +2404,7 @@ var Body = Backgrid.Body = Backbone.View.extend({
See [Backbone.Collection#comparator](http://backbonejs.org/#Collection-comparator)
*/
sort: function (column, direction) {
//debugger;
if (_.isString(column)) column = this.columns.findWhere({name: column});
var collection = this.collection;
@ -2761,4 +2761,4 @@ var Grid = Backgrid.Grid = Backbone.View.extend({
});
return Backgrid;
}));
}));

@ -815,7 +815,7 @@
sort: function(options) {
if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
options || (options = {});
//debugger;
// Run sort based on type of `comparator`.
if (_.isString(this.comparator) || this.comparator.length === 1) {
this.models = this.sortBy(this.comparator, this);

@ -324,9 +324,11 @@
if (comparator && options.full) {
this.comparator = null;
fullCollection.comparator = comparator;
} else if (options.full){
fullCollection.comparator = this.comparator;
}
if (options.full) fullCollection.sort();
//if (options.full) fullCollection.sort();
// make sure the models in the current page and full collection have the
// same references

@ -15,6 +15,7 @@ var MovieStatusCell = require('../../Cells/MovieStatusCell');
var MovieDownloadStatusCell = require('../../Cells/MovieDownloadStatusCell');
var DownloadedQualityCell = require('../../Cells/DownloadedQualityCell');
var FooterView = require('./FooterView');
var GridPager = require('../../Shared/Grid/Pager');
var FooterModel = require('./FooterModel');
var ToolbarLayout = require('../../Shared/Toolbar/ToolbarLayout');
require('../../Mixins/backbone.signalr.mixin');
@ -26,12 +27,14 @@ module.exports = Marionette.Layout.extend({
seriesRegion : '#x-series',
toolbar : '#x-toolbar',
toolbar2 : '#x-toolbar2',
footer : '#x-series-footer'
footer : '#x-series-footer',
pager : "#x-movie-pager",
pagerTop : "#x-movie-pager-top"
},
columns : [
{
name : 'statusWeight',
name : 'status',
label : '',
cell : MovieStatusCell
},
@ -45,6 +48,7 @@ module.exports = Marionette.Layout.extend({
name : "downloadedQuality",
label : "Downloaded",
cell : DownloadedQualityCell,
sortable : true
},
{
name : 'profileId',
@ -67,6 +71,7 @@ module.exports = Marionette.Layout.extend({
name : "this",
label : "Status",
cell : MovieDownloadStatusCell,
sortable : false,
sortValue : function(m, k) {
if (m.get("downloaded")) {
return -1;
@ -115,31 +120,40 @@ module.exports = Marionette.Layout.extend({
initialize : function() {
this.seriesCollection = MoviesCollection.clone();
this.seriesCollection.shadowCollection.bindSignalR();
this.seriesCollection.bindSignalR();
this.listenTo(this.seriesCollection.shadowCollection, 'sync', function(model, collection, options) {
this.seriesCollection.fullCollection.resetFiltered();
this.listenTo(this.seriesCollection, 'sync', function(model, collection, options) {
//this.seriesCollection.fullCollection.resetFiltered();
this._renderView();
});
this.listenTo(this.seriesCollection.shadowCollection, 'add', function(model, collection, options) {
this.seriesCollection.fullCollection.resetFiltered();
this._renderView();
this.listenTo(MoviesCollection, "sync", function(eventName) {
this.seriesCollection = MoviesCollection.clone();
//this._showTable();
this._renderView();
});
this.listenTo(this.seriesCollection.shadowCollection, 'remove', function(model, collection, options) {
this.seriesCollection.fullCollection.resetFiltered();
this._renderView();
this.listenTo(this.seriesCollection, 'add', function(model, collection, options) {
//this.seriesCollection.fullCollection.resetFiltered();
//this._renderView();
});
this.listenTo(this.seriesCollection, 'remove', function(model, collection, options) {
//this.seriesCollection.fullCollection.resetFiltered();
//this._showTable();
});
this.sortingOptions = {
type : 'sorting',
storeState : false,
viewCollection : this.seriesCollection,
callback : this._sort,
items : [
{
title : 'Title',
name : 'sortTitle'
name : 'title'
},
{
title: 'Downloaded',
@ -153,10 +167,10 @@ module.exports = Marionette.Layout.extend({
title : 'In Cinemas',
name : 'inCinemas'
},
{
/*{
title : "Status",
name : "status",
}
}*/
]
};
@ -254,10 +268,13 @@ module.exports = Marionette.Layout.extend({
className : 'table table-hover'
});
this._showPager();
this._renderView();
},
_showList : function() {
//this.current = "list";
this.currentView = new ListCollectionView({
collection : this.seriesCollection
});
@ -273,6 +290,10 @@ module.exports = Marionette.Layout.extend({
this._renderView();
},
_sort : function() {
console.warn("Sorting");
},
_renderView : function() {
if (MoviesCollection.length === 0) {
this.seriesRegion.show(new EmptyView());
@ -280,8 +301,12 @@ module.exports = Marionette.Layout.extend({
this.toolbar.close();
this.toolbar2.close();
} else {
this.seriesRegion.show(this.currentView);
this.seriesRegion.show(this.currentView);
this.listenTo(this.currentView.collection, "sync", function(eventName){
this._showPager();
//debugger;
});
this._showToolbar();
this._showFooter();
}
@ -293,7 +318,6 @@ module.exports = Marionette.Layout.extend({
_setFilter : function(buttonContext) {
var mode = buttonContext.model.get('key');
this.seriesCollection.setFilterMode(mode);
},
@ -321,6 +345,19 @@ module.exports = Marionette.Layout.extend({
}));
},
_showPager : function() {
var pager = new GridPager({
columns : this.columns,
collection : this.seriesCollection,
});
var pagerTop = new GridPager({
columns : this.columns,
collection : this.seriesCollection,
});
this.pager.show(pager);
this.pagerTop.show(pagerTop);
},
_showFooter : function() {
var footerModel = new FooterModel();
var movies = MoviesCollection.models.length;
@ -330,12 +367,12 @@ module.exports = Marionette.Layout.extend({
var released = 0;
var monitored = 0;
var downloaded =0;
var missingMonitored=0;
var missingNotMonitored=0;
var missingNotAvailable=0;
var missingMonitoredAvailable=0;
var missingMonitoredAvailable=0;
var downloadedNotMonitored=0;

@ -3,10 +3,16 @@
<div id="x-toolbar2"></div>
</div>
<div id="x-movie-pager-top">
</div>
<div class="row">
<div class="col-md-12">
<div id="x-series" class="table-responsive"></div>
</div>
</div>
<div id="x-movie-pager">
</div>
<div id="x-series-footer"></div>

@ -7,22 +7,70 @@ var AsFilteredCollection = require('../Mixins/AsFilteredCollection');
var AsSortedCollection = require('../Mixins/AsSortedCollection');
var AsPersistedStateCollection = require('../Mixins/AsPersistedStateCollection');
var moment = require('moment');
var UiSettings = require('../Shared/UiSettingsModel');
require('../Mixins/backbone.signalr.mixin');
var Config = require('../Config');
var pageSize = parseInt(Config.getValue("pageSize")) || 1000;
var Collection = PageableCollection.extend({
url : window.NzbDrone.ApiRoot + '/movie',
model : MovieModel,
tableName : 'movie',
origSetSorting : PageableCollection.prototype.setSorting,
origAdd : PageableCollection.prototype.add,
state : {
sortKey : 'sortTitle',
order : -1,
pageSize : 100000,
pageSize : pageSize,
secondarySortKey : 'sortTitle',
secondarySortOrder : -1
},
mode : 'client',
queryParams : {
totalPages : null,
totalRecords : null,
pageSize : 'pageSize',
sortKey : 'sortKey',
order : 'sortDir',
directions : {
'-1' : 'asc',
'1' : 'desc'
}
},
sortMappings : {
'movie' : { sortKey : 'series.sortTitle' }
},
parseState : function(resp) {
var direction = -1;
if (resp.sortDirection == "descending") {
direction = 1;
}
return { totalRecords : resp.totalRecords, order : direction, currentPage : resp.page };
},
parseRecords : function(resp) {
if (resp) {
return resp.records;
}
return resp;
},
mode : 'server',
setSorting : function(sortKey, order, options) {
return this.origSetSorting.call(this, sortKey, order, options);
},
sort : function(options){
//debugger;
},
save : function() {
var self = this;
@ -90,18 +138,18 @@ var Collection = PageableCollection.extend({
false
],
'released' : [
null,
null,
"status",
"released",
function(model) { return model.getStatus() == "released"; }
],
'announced' : [
null,
null,
"status",
"announced",
function(model) { return model.getStatus() == "announced"; }
],
'cinemas' : [
null,
null,
"status",
"inCinemas",
function(model) { return model.getStatus() == "inCinemas"; }
]
},
@ -127,10 +175,10 @@ var Collection = PageableCollection.extend({
downloadedQuality : {
sortValue : function(model, attr) {
if (model.get("movieFile")) {
return 1000-model.get("movieFile").quality.quality.id;
return model.get("movieFile").quality.quality.name;
}
return -1;
return "";
}
},
nextAiring : {
@ -184,6 +232,24 @@ var Collection = PageableCollection.extend({
return path.toLowerCase();
}
}
},
add : function(model, options) {
if (this.length >= this.state.pageSize) {
return;
}
this.origAdd.call(this, model, options);
},
setFilterMode : function(mode){
var arr = this.filterModes[mode];
this.state.filterKey = arr[0];
this.state.filterValue = arr[1];
this.fetch();
},
comparator: function (model) {
return model.get('sortTitle');
}
});
@ -191,6 +257,6 @@ Collection = AsFilteredCollection.call(Collection);
Collection = AsSortedCollection.call(Collection);
Collection = AsPersistedStateCollection.call(Collection);
var data = ApiData.get('movie');
var data = ApiData.get('movie?page=1&pageSize='+pageSize+'&sortKey=sortTitle&sortDir=asc');
module.exports = new Collection(data, { full : true }).bindSignalR();
module.exports = new Collection(data.records, { full : false, state : { totalRecords : data.totalRecords} }).bindSignalR();

@ -8,6 +8,7 @@ var model = DeepModel.extend({
initialize : function() {
this.listenTo(vent, vent.Commands.SaveSettings, this.saveSettings);
this.listenTo(this, 'destroy', this._stopListening);
},
saveSettings : function() {

@ -1,7 +1,21 @@
var SettingsModelBase = require('../SettingsModelBase');
var Config = require('../../Config');
module.exports = SettingsModelBase.extend({
url : window.NzbDrone.ApiRoot + '/config/ui',
successMessage : 'UI settings saved',
errorMessage : 'Failed to save UI settings'
});
errorMessage : 'Failed to save UI settings',
origSave : SettingsModelBase.prototype.saveSettings,
origInit : SettingsModelBase.prototype.initialize,
initialize : function() {
this.set("pageSize", Config.getValue("pageSize"));
this.origInit.call(this);
},
saveSettings : function() {
Config.setValue("pageSize", this.get("pageSize"));
this.origSave.call(this);
}
});

@ -1,4 +1,27 @@
<div class="form-horizontal">
<fieldset>
<legend>Movies</legend>
<div class="form-group">
<label class="col-sm-3 control-label">Page Size</label>
<div class="col-sm-4">
<select name="pageSize" class="form-control">
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
<option value="250">250</option>
<option value="500">500</option>
<option value="1000">1000</option>
</select>
</div>
<span class="col-sm-1 help-inline">
<i class="icon-sonarr-form-info" title="How many movies to show on the main page."/>
</span>
</div>
</fieldset>
<fieldset>
<legend>Calendar</legend>

@ -72,6 +72,8 @@ module.exports = Paginator.extend({
var handles = [];
var collection = this.collection;
var state = collection.state;
// convert all indices to 0-based here

@ -31,8 +31,13 @@ module.exports = Marionette.CompositeView.extend({
}
collection.setSorting(sortModel.get('name'), order);
collection.fullCollection.sort();
if (collection.mode == "server"){
collection.fetch({reset: true});
} else {
collection.fullCollection.sort();
}
return this;
}
});
});

Loading…
Cancel
Save