mirror of
https://github.com/lidarr/lidarr.git
synced 2025-08-23 14:55:20 -07:00
Wanted & Missing (#687)
* Remove Season Pass, Update Header name, remove useless function * Cutoff Tab now works
This commit is contained in:
parent
2af07d7e0d
commit
f477c46406
9 changed files with 165 additions and 55 deletions
|
@ -267,6 +267,7 @@
|
||||||
<Compile Include="Wanted\CutoffModule.cs" />
|
<Compile Include="Wanted\CutoffModule.cs" />
|
||||||
<Compile Include="Wanted\LegacyMissingModule.cs" />
|
<Compile Include="Wanted\LegacyMissingModule.cs" />
|
||||||
<Compile Include="Wanted\MissingModule.cs" />
|
<Compile Include="Wanted\MissingModule.cs" />
|
||||||
|
<Compile Include="Wanted\MovieCutoffModule.cs" />
|
||||||
<Compile Include="Wanted\MovieMissingModule.cs" />
|
<Compile Include="Wanted\MovieMissingModule.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
48
src/NzbDrone.Api/Wanted/MovieCutoffModule.cs
Normal file
48
src/NzbDrone.Api/Wanted/MovieCutoffModule.cs
Normal file
|
@ -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<MovieResource, Core.Tv.Movie>
|
||||||
|
{
|
||||||
|
private readonly IMovieCutoffService _movieCutoffService;
|
||||||
|
|
||||||
|
public MovieCutoffModule(IMovieCutoffService movieCutoffService,
|
||||||
|
IMovieService movieService,
|
||||||
|
IQualityUpgradableSpecification qualityUpgradableSpecification,
|
||||||
|
IBroadcastSignalRMessage signalRBroadcaster)
|
||||||
|
: base(signalRBroadcaster, "wanted/cutoff")
|
||||||
|
{
|
||||||
|
_movieCutoffService = movieCutoffService;
|
||||||
|
GetResourcePaged = GetCutoffUnmetMovies;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PagingResource<MovieResource> GetCutoffUnmetMovies(PagingResource<MovieResource> pagingResource)
|
||||||
|
{
|
||||||
|
var pagingSpec = pagingResource.MapToPagingSpec<MovieResource, Core.Tv.Movie>("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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,7 +30,7 @@ namespace NzbDrone.Api.Wanted
|
||||||
|
|
||||||
private PagingResource<MovieResource> GetMissingMovies(PagingResource<MovieResource> pagingResource)
|
private PagingResource<MovieResource> GetMissingMovies(PagingResource<MovieResource> pagingResource)
|
||||||
{
|
{
|
||||||
var pagingSpec = pagingResource.MapToPagingSpec<MovieResource, Core.Tv.Movie>("physicalRelease", SortDirection.Descending);
|
var pagingSpec = pagingResource.MapToPagingSpec<MovieResource, Core.Tv.Movie>("title", SortDirection.Descending);
|
||||||
|
|
||||||
if (pagingResource.FilterKey == "monitored" && pagingResource.FilterValue == "false")
|
if (pagingResource.FilterKey == "monitored" && pagingResource.FilterValue == "false")
|
||||||
{
|
{
|
||||||
|
@ -46,13 +46,6 @@ namespace NzbDrone.Api.Wanted
|
||||||
return resource;
|
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)
|
private MovieResource MapToResource(Core.Tv.Movie movie, bool includeMovieFile)
|
||||||
{
|
{
|
||||||
var resource = movie.ToResource();
|
var resource = movie.ToResource();
|
||||||
|
|
|
@ -1164,6 +1164,7 @@
|
||||||
<Compile Include="Tv\Events\SeriesUpdatedEvent.cs" />
|
<Compile Include="Tv\Events\SeriesUpdatedEvent.cs" />
|
||||||
<Compile Include="Tv\MonitoringOptions.cs" />
|
<Compile Include="Tv\MonitoringOptions.cs" />
|
||||||
<Compile Include="Tv\MoveSeriesService.cs" />
|
<Compile Include="Tv\MoveSeriesService.cs" />
|
||||||
|
<Compile Include="Tv\MovieCutoffService.cs" />
|
||||||
<Compile Include="Tv\Ratings.cs" />
|
<Compile Include="Tv\Ratings.cs" />
|
||||||
<Compile Include="Tv\RefreshEpisodeService.cs" />
|
<Compile Include="Tv\RefreshEpisodeService.cs" />
|
||||||
<Compile Include="Tv\RefreshMovieService.cs" />
|
<Compile Include="Tv\RefreshMovieService.cs" />
|
||||||
|
|
48
src/NzbDrone.Core/Tv/MovieCutoffService.cs
Normal file
48
src/NzbDrone.Core/Tv/MovieCutoffService.cs
Normal file
|
@ -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<Movie> MoviesWhereCutoffUnmet(PagingSpec<Movie> 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<Movie> MoviesWhereCutoffUnmet(PagingSpec<Movie> pagingSpec)
|
||||||
|
{
|
||||||
|
var qualitiesBelowCutoff = new List<QualitiesBelowCutoff>();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Core.Datastore.Extensions;
|
using NzbDrone.Core.Datastore.Extensions;
|
||||||
using Marr.Data.QGen;
|
using Marr.Data.QGen;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
|
using NzbDrone.Core.Qualities;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Tv
|
namespace NzbDrone.Core.Tv
|
||||||
{
|
{
|
||||||
|
@ -22,6 +23,7 @@ namespace NzbDrone.Core.Tv
|
||||||
PagingSpec<Movie> MoviesWithoutFiles(PagingSpec<Movie> pagingSpec);
|
PagingSpec<Movie> MoviesWithoutFiles(PagingSpec<Movie> pagingSpec);
|
||||||
List<Movie> GetMoviesByFileId(int fileId);
|
List<Movie> GetMoviesByFileId(int fileId);
|
||||||
void SetFileId(int fileId, int movieId);
|
void SetFileId(int fileId, int movieId);
|
||||||
|
PagingSpec<Movie> MoviesWhereCutoffUnmet(PagingSpec<Movie> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MovieRepository : BasicRepository<Movie>, IMovieRepository
|
public class MovieRepository : BasicRepository<Movie>, IMovieRepository
|
||||||
|
@ -200,6 +202,41 @@ namespace NzbDrone.Core.Tv
|
||||||
.Take(pagingSpec.PageSize);
|
.Take(pagingSpec.PageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PagingSpec<Movie> MoviesWhereCutoffUnmet(PagingSpec<Movie> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff)
|
||||||
|
{
|
||||||
|
|
||||||
|
pagingSpec.TotalRecords = MoviesWhereCutoffUnmetQuery(pagingSpec, qualitiesBelowCutoff).GetRowCount();
|
||||||
|
pagingSpec.Records = MoviesWhereCutoffUnmetQuery(pagingSpec, qualitiesBelowCutoff).ToList();
|
||||||
|
|
||||||
|
return pagingSpec;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SortBuilder<Movie> MoviesWhereCutoffUnmetQuery(PagingSpec<Movie> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff)
|
||||||
|
{
|
||||||
|
return Query.Join<Movie, MovieFile>(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> qualitiesBelowCutoff)
|
||||||
|
{
|
||||||
|
var clauses = new List<string>();
|
||||||
|
|
||||||
|
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)
|
public Movie FindByTmdbId(int tmdbid)
|
||||||
{
|
{
|
||||||
return Query.Where(m => m.TmdbId == tmdbid).FirstOrDefault();
|
return Query.Where(m => m.TmdbId == tmdbid).FirstOrDefault();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
var EpisodeModel = require('../../Series/EpisodeModel');
|
var MovieModel = require('../../Movies/MovieModel');
|
||||||
var PagableCollection = require('backbone.pageable');
|
var PagableCollection = require('backbone.pageable');
|
||||||
var AsFilteredCollection = require('../../Mixins/AsFilteredCollection');
|
var AsFilteredCollection = require('../../Mixins/AsFilteredCollection');
|
||||||
var AsSortedCollection = require('../../Mixins/AsSortedCollection');
|
var AsSortedCollection = require('../../Mixins/AsSortedCollection');
|
||||||
|
@ -7,13 +7,13 @@ var AsPersistedStateCollection = require('../../Mixins/AsPersistedStateCollectio
|
||||||
|
|
||||||
var Collection = PagableCollection.extend({
|
var Collection = PagableCollection.extend({
|
||||||
url : window.NzbDrone.ApiRoot + '/wanted/cutoff',
|
url : window.NzbDrone.ApiRoot + '/wanted/cutoff',
|
||||||
model : EpisodeModel,
|
model : MovieModel,
|
||||||
tableName : 'wanted.cutoff',
|
tableName : 'wanted.cutoff',
|
||||||
|
|
||||||
state : {
|
state : {
|
||||||
pageSize : 15,
|
pageSize : 15,
|
||||||
sortKey : 'airDateUtc',
|
sortKey : 'title',
|
||||||
order : 1
|
order : -1
|
||||||
},
|
},
|
||||||
|
|
||||||
queryParams : {
|
queryParams : {
|
||||||
|
@ -40,9 +40,9 @@ var Collection = PagableCollection.extend({
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
sortMappings : {
|
// sortMappings : {
|
||||||
'series' : { sortKey : 'series.sortTitle' }
|
// 'this' : { sortKey : 'this.sortTitle' }
|
||||||
},
|
// },
|
||||||
|
|
||||||
parseState : function(resp) {
|
parseState : function(resp) {
|
||||||
return { totalRecords : resp.totalRecords };
|
return { totalRecords : resp.totalRecords };
|
||||||
|
|
|
@ -3,16 +3,15 @@ var Marionette = require('marionette');
|
||||||
var Backgrid = require('backgrid');
|
var Backgrid = require('backgrid');
|
||||||
var CutoffUnmetCollection = require('./CutoffUnmetCollection');
|
var CutoffUnmetCollection = require('./CutoffUnmetCollection');
|
||||||
var SelectAllCell = require('../../Cells/SelectAllCell');
|
var SelectAllCell = require('../../Cells/SelectAllCell');
|
||||||
var SeriesTitleCell = require('../../Cells/SeriesTitleCell');
|
var MovieTitleCell = require('../../Cells/MovieTitleCell');
|
||||||
var EpisodeNumberCell = require('../../Cells/EpisodeNumberCell');
|
var MovieStatusWithTextCell = require('../../Cells/MovieStatusWithTextCell');
|
||||||
var EpisodeTitleCell = require('../../Cells/EpisodeTitleCell');
|
|
||||||
var RelativeDateCell = require('../../Cells/RelativeDateCell');
|
var RelativeDateCell = require('../../Cells/RelativeDateCell');
|
||||||
var EpisodeStatusCell = require('../../Cells/EpisodeStatusCell');
|
|
||||||
var GridPager = require('../../Shared/Grid/Pager');
|
var GridPager = require('../../Shared/Grid/Pager');
|
||||||
var ToolbarLayout = require('../../Shared/Toolbar/ToolbarLayout');
|
var ToolbarLayout = require('../../Shared/Toolbar/ToolbarLayout');
|
||||||
var LoadingView = require('../../Shared/LoadingView');
|
var LoadingView = require('../../Shared/LoadingView');
|
||||||
var Messenger = require('../../Shared/Messenger');
|
var Messenger = require('../../Shared/Messenger');
|
||||||
var CommandController = require('../../Commands/CommandController');
|
var CommandController = require('../../Commands/CommandController');
|
||||||
|
|
||||||
require('backgrid.selectall');
|
require('backgrid.selectall');
|
||||||
require('../../Mixins/backbone.signalr.mixin');
|
require('../../Mixins/backbone.signalr.mixin');
|
||||||
|
|
||||||
|
@ -36,33 +35,26 @@ module.exports = Marionette.Layout.extend({
|
||||||
headerCell : 'select-all',
|
headerCell : 'select-all',
|
||||||
sortable : false
|
sortable : false
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name : 'series',
|
|
||||||
label : 'Series Title',
|
|
||||||
cell : SeriesTitleCell,
|
|
||||||
sortValue : 'series.sortTitle'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name : 'this',
|
name : 'this',
|
||||||
label : 'Episode',
|
label : 'Movie Title',
|
||||||
cell : EpisodeNumberCell,
|
cell : MovieTitleCell,
|
||||||
sortable : false
|
sortValue : this.sortTitle
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name : 'this',
|
name : 'inCinemas',
|
||||||
label : 'Episode Title',
|
label : 'In Cinemas',
|
||||||
cell : EpisodeTitleCell,
|
cell : RelativeDateCell
|
||||||
sortable : false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name : 'airDateUtc',
|
name : 'physicalRelease',
|
||||||
label : 'Air Date',
|
label : 'Physical Release',
|
||||||
cell : RelativeDateCell
|
cell : RelativeDateCell
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name : 'status',
|
name : 'status',
|
||||||
label : 'Status',
|
label : 'Status',
|
||||||
cell : EpisodeStatusCell,
|
cell : MovieStatusWithTextCell,
|
||||||
sortable : false
|
sortable : false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -105,11 +97,6 @@ module.exports = Marionette.Layout.extend({
|
||||||
callback : this._searchSelected,
|
callback : this._searchSelected,
|
||||||
ownerContext : this,
|
ownerContext : this,
|
||||||
className : 'x-search-selected'
|
className : 'x-search-selected'
|
||||||
},
|
|
||||||
{
|
|
||||||
title : 'Season Pass',
|
|
||||||
icon : 'icon-sonarr-monitored',
|
|
||||||
route : 'seasonpass'
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
@ -150,7 +137,7 @@ module.exports = Marionette.Layout.extend({
|
||||||
CommandController.bindToCommand({
|
CommandController.bindToCommand({
|
||||||
element : this.$('.x-search-selected'),
|
element : this.$('.x-search-selected'),
|
||||||
command : {
|
command : {
|
||||||
name : 'episodeSearch'
|
name : 'moviesSearch'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -172,7 +159,7 @@ module.exports = Marionette.Layout.extend({
|
||||||
if (selected.length === 0) {
|
if (selected.length === 0) {
|
||||||
Messenger.show({
|
Messenger.show({
|
||||||
type : 'error',
|
type : 'error',
|
||||||
message : 'No episodes selected'
|
message : 'No movies selected'
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -180,9 +167,9 @@ module.exports = Marionette.Layout.extend({
|
||||||
|
|
||||||
var ids = _.pluck(selected, 'id');
|
var ids = _.pluck(selected, 'id');
|
||||||
|
|
||||||
CommandController.Execute('episodeSearch', {
|
CommandController.Execute('moviesSearch', {
|
||||||
name : 'episodeSearch',
|
name : 'moviesSearch',
|
||||||
episodeIds : ids
|
movieIds : ids
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -115,11 +115,6 @@ module.exports = Marionette.Layout.extend({
|
||||||
ownerContext : this,
|
ownerContext : this,
|
||||||
className : 'x-unmonitor-selected'
|
className : 'x-unmonitor-selected'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title : 'Season Pass',
|
|
||||||
icon : 'icon-sonarr-monitored',
|
|
||||||
route : 'seasonpass'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title : 'Rescan Drone Factory Folder',
|
title : 'Rescan Drone Factory Folder',
|
||||||
icon : 'icon-sonarr-refresh',
|
icon : 'icon-sonarr-refresh',
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue