mirror of
https://github.com/Radarr/Radarr
synced 2025-02-23 06:41:20 +00:00
New: Added filter and sort options to Collections (#8731)
* New: Added filter and sort options to Collections * Add AllMovieWithCollectionsTmdbIds method to MovieService and MovieRepository
This commit is contained in:
parent
fed98a648f
commit
cbae355402
7 changed files with 62 additions and 19 deletions
|
@ -2,17 +2,12 @@ import PropTypes from 'prop-types';
|
|||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createAllMoviesSelector from 'Store/Selectors/createAllMoviesSelector';
|
||||
import createCollectionSelector from 'Store/Selectors/createCollectionSelector';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createCollectionSelector(),
|
||||
createAllMoviesSelector(),
|
||||
(
|
||||
collection,
|
||||
allMovies
|
||||
) => {
|
||||
(collection) => {
|
||||
// If a movie is deleted this selector may fire before the parent
|
||||
// selecors, which will result in an undefined movie, if that happens
|
||||
// we want to return early here and again in the render function to avoid
|
||||
|
@ -22,21 +17,11 @@ function createMapStateToProps() {
|
|||
return {};
|
||||
}
|
||||
|
||||
let allGenres = [];
|
||||
let libraryMovies = 0;
|
||||
|
||||
collection.movies.forEach((movie) => {
|
||||
allGenres = allGenres.concat(movie.genres);
|
||||
|
||||
if (allMovies.find((libraryMovie) => libraryMovie.tmdbId === movie.tmdbId)) {
|
||||
libraryMovies++;
|
||||
}
|
||||
});
|
||||
const allGenres = collection.movies.flatMap((movie) => movie.genres);
|
||||
|
||||
return {
|
||||
...collection,
|
||||
genres: Array.from(new Set(allGenres)).slice(0, 3),
|
||||
missingMovies: collection.movies.length - libraryMovies
|
||||
genres: Array.from(new Set(allGenres)).slice(0, 3)
|
||||
};
|
||||
}
|
||||
);
|
||||
|
|
|
@ -28,6 +28,14 @@ function CollectionSortMenu(props) {
|
|||
>
|
||||
{translate('Title')}
|
||||
</SortMenuItem>
|
||||
<SortMenuItem
|
||||
name="missingMovies"
|
||||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
onPress={onSortSelect}
|
||||
>
|
||||
{translate('Missing')}
|
||||
</SortMenuItem>
|
||||
</MenuContent>
|
||||
</SortMenu>
|
||||
);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import _ from 'lodash';
|
||||
import { createAction } from 'redux-actions';
|
||||
import { batchActions } from 'redux-batched-actions';
|
||||
import { filterBuilderTypes, filterBuilderValueTypes, filterTypePredicates, sortDirections } from 'Helpers/Props';
|
||||
import { filterBuilderTypes, filterBuilderValueTypes, filterTypePredicates, filterTypes, sortDirections } from 'Helpers/Props';
|
||||
import { createThunk, handleThunks } from 'Store/thunks';
|
||||
import sortByName from 'Utilities/Array/sortByName';
|
||||
import createAjaxRequest from 'Utilities/createAjaxRequest';
|
||||
|
@ -62,6 +62,28 @@ export const defaultState = {
|
|||
key: 'all',
|
||||
label: 'All',
|
||||
filters: []
|
||||
},
|
||||
{
|
||||
key: 'missing',
|
||||
label: 'Missing',
|
||||
filters: [
|
||||
{
|
||||
key: 'missingMovies',
|
||||
value: 0,
|
||||
type: filterTypes.GREATER_THAN
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'complete',
|
||||
label: 'Complete',
|
||||
filters: [
|
||||
{
|
||||
key: 'missingMovies',
|
||||
value: 0,
|
||||
type: filterTypes.EQUAL
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ public interface IMovieRepository : IBasicRepository<Movie>
|
|||
Dictionary<int, List<int>> AllMovieTags();
|
||||
List<int> GetRecommendations();
|
||||
bool ExistsByMetadataId(int metadataId);
|
||||
HashSet<int> AllMovieWithCollectionsTmdbIds();
|
||||
}
|
||||
|
||||
public class MovieRepository : BasicRepository<Movie>, IMovieRepository
|
||||
|
@ -373,5 +374,13 @@ public bool ExistsByMetadataId(int metadataId)
|
|||
|
||||
return movies.Any();
|
||||
}
|
||||
|
||||
public HashSet<int> AllMovieWithCollectionsTmdbIds()
|
||||
{
|
||||
using (var conn = _database.OpenConnection())
|
||||
{
|
||||
return conn.Query<int>("SELECT \"TmdbId\" FROM \"MovieMetadata\" JOIN \"Movies\" ON (\"Movies\".\"MovieMetadataId\" = \"MovieMetadata\".\"Id\") WHERE \"CollectionTmdbId\" > 0").ToHashSet();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ public interface IMovieService
|
|||
bool MoviePathExists(string folder);
|
||||
void RemoveAddOptions(Movie movie);
|
||||
bool ExistsByMetadataId(int metadataId);
|
||||
HashSet<int> AllMovieWithCollectionsTmdbIds();
|
||||
}
|
||||
|
||||
public class MovieService : IMovieService, IHandle<MovieFileAddedEvent>,
|
||||
|
@ -390,6 +391,11 @@ public bool ExistsByMetadataId(int metadataId)
|
|||
return _movieRepository.ExistsByMetadataId(metadataId);
|
||||
}
|
||||
|
||||
public HashSet<int> AllMovieWithCollectionsTmdbIds()
|
||||
{
|
||||
return _movieRepository.AllMovieWithCollectionsTmdbIds();
|
||||
}
|
||||
|
||||
private Movie ReturnSingleMovieOrThrow(List<Movie> movies)
|
||||
{
|
||||
if (movies.Count == 0)
|
||||
|
|
|
@ -135,6 +135,7 @@ private IEnumerable<CollectionResource> MapToResource(List<MovieCollection> coll
|
|||
// Avoid calling for naming spec on every movie in filenamebuilder
|
||||
var namingConfig = _namingService.GetConfig();
|
||||
var collectionMovies = _movieMetadataService.GetMoviesWithCollections();
|
||||
var existingMoviesTmdbIds = _movieService.AllMovieWithCollectionsTmdbIds();
|
||||
|
||||
foreach (var collection in collections)
|
||||
{
|
||||
|
@ -145,6 +146,11 @@ private IEnumerable<CollectionResource> MapToResource(List<MovieCollection> coll
|
|||
var movieResource = movie.ToResource();
|
||||
movieResource.Folder = _fileNameBuilder.GetMovieFolder(new Movie { MovieMetadata = movie }, namingConfig);
|
||||
|
||||
if (!existingMoviesTmdbIds.Contains(movie.TmdbId))
|
||||
{
|
||||
resource.MissingMovies++;
|
||||
}
|
||||
|
||||
resource.Movies.Add(movieResource);
|
||||
}
|
||||
|
||||
|
@ -155,12 +161,18 @@ private IEnumerable<CollectionResource> MapToResource(List<MovieCollection> coll
|
|||
private CollectionResource MapToResource(MovieCollection collection)
|
||||
{
|
||||
var resource = collection.ToResource();
|
||||
var existingMoviesTmdbIds = _movieService.AllMovieWithCollectionsTmdbIds();
|
||||
|
||||
foreach (var movie in _movieMetadataService.GetMoviesByCollectionTmdbId(collection.TmdbId))
|
||||
{
|
||||
var movieResource = movie.ToResource();
|
||||
movieResource.Folder = _fileNameBuilder.GetMovieFolder(new Movie { MovieMetadata = movie });
|
||||
|
||||
if (!existingMoviesTmdbIds.Contains(movie.TmdbId))
|
||||
{
|
||||
resource.MissingMovies++;
|
||||
}
|
||||
|
||||
resource.Movies.Add(movieResource);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ public CollectionResource()
|
|||
public bool SearchOnAdd { get; set; }
|
||||
public MovieStatusType MinimumAvailability { get; set; }
|
||||
public List<CollectionMovieResource> Movies { get; set; }
|
||||
public int MissingMovies { get; set; }
|
||||
}
|
||||
|
||||
public static class CollectionResourceMapper
|
||||
|
|
Loading…
Reference in a new issue