1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-09-11 20:12:41 +02:00

Fixed: Wanted/Cutoff Search from Index Page

This commit is contained in:
Qstick 2019-07-25 22:34:24 -04:00
parent 6705b59b23
commit d263ff9a6b
7 changed files with 161 additions and 10 deletions

View File

@ -3,12 +3,12 @@ export const BACKUP = 'Backup';
export const CHECK_FOR_FINISHED_DOWNLOAD = 'CheckForFinishedDownload'; export const CHECK_FOR_FINISHED_DOWNLOAD = 'CheckForFinishedDownload';
export const CLEAR_BLACKLIST = 'ClearBlacklist'; export const CLEAR_BLACKLIST = 'ClearBlacklist';
export const CLEAR_LOGS = 'ClearLog'; export const CLEAR_LOGS = 'ClearLog';
export const CUTOFF_UNMET_EPISODE_SEARCH = 'CutoffUnmetEpisodeSearch'; export const CUTOFF_UNMET_MOVIES_SEARCH = 'CutoffUnmetMoviesSearch';
export const DELETE_LOG_FILES = 'DeleteLogFiles'; export const DELETE_LOG_FILES = 'DeleteLogFiles';
export const DELETE_UPDATE_LOG_FILES = 'DeleteUpdateLogFiles'; export const DELETE_UPDATE_LOG_FILES = 'DeleteUpdateLogFiles';
export const DOWNLOADED_EPSIODES_SCAN = 'DownloadedEpisodesScan'; export const DOWNLOADED_EPSIODES_SCAN = 'DownloadedEpisodesScan';
export const INTERACTIVE_IMPORT = 'ManualImport'; export const INTERACTIVE_IMPORT = 'ManualImport';
export const MISSING_EPISODE_SEARCH = 'MissingEpisodeSearch'; export const MISSING_MOVIES_SEARCH = 'MissingMoviesSearch';
export const MOVE_MOVIE = 'MoveMovie'; export const MOVE_MOVIE = 'MoveMovie';
export const REFRESH_MOVIE = 'RefreshMovie'; export const REFRESH_MOVIE = 'RefreshMovie';
export const RENAME_FILES = 'RenameFiles'; export const RENAME_FILES = 'RenameFiles';

View File

@ -0,0 +1,47 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import MenuItem from './MenuItem';
class SearchMenuItem extends Component {
//
// Listeners
onPress = () => {
const {
name,
onPress
} = this.props;
onPress(name);
}
//
// Render
render() {
const {
children,
...otherProps
} = this.props;
return (
<MenuItem
{...otherProps}
onPress={this.onPress}
>
<div>
{children}
</div>
</MenuItem>
);
}
}
SearchMenuItem.propTypes = {
name: PropTypes.string,
children: PropTypes.node.isRequired,
onPress: PropTypes.func.isRequired
};
export default SearchMenuItem;

View File

@ -0,0 +1,52 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { align, icons } from 'Helpers/Props';
import Menu from 'Components/Menu/Menu';
import MenuContent from 'Components/Menu/MenuContent';
import ToolbarMenuButton from 'Components/Menu/ToolbarMenuButton';
import SearchMenuItem from 'Components/Menu/SearchMenuItem';
class MovieIndexSearchMenu extends Component {
render() {
const {
isDisabled,
onSearchPress
} = this.props;
return (
<Menu
isDisabled={isDisabled}
alignMenu={align.RIGHT}
>
<ToolbarMenuButton
iconName={icons.SEARCH}
text="Search"
isDisabled={isDisabled}
/>
<MenuContent>
<SearchMenuItem
name="missingMoviesSearch"
onPress={onSearchPress}
>
Search Missing
</SearchMenuItem>
<SearchMenuItem
name="cutoffUnmetMoviesSearch"
onPress={onSearchPress}
>
Search Cutoff Unmet
</SearchMenuItem>
</MenuContent>
</Menu>
);
}
}
MovieIndexSearchMenu.propTypes = {
isDisabled: PropTypes.bool.isRequired,
onSearchPress: PropTypes.func.isRequired
};
export default MovieIndexSearchMenu;

View File

@ -5,7 +5,7 @@ import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
import getSelectedIds from 'Utilities/Table/getSelectedIds'; import getSelectedIds from 'Utilities/Table/getSelectedIds';
import selectAll from 'Utilities/Table/selectAll'; import selectAll from 'Utilities/Table/selectAll';
import toggleSelected from 'Utilities/Table/toggleSelected'; import toggleSelected from 'Utilities/Table/toggleSelected';
import { align, icons, sortDirections } from 'Helpers/Props'; import { align, icons, kinds, sortDirections } from 'Helpers/Props';
import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import PageContent from 'Components/Page/PageContent'; import PageContent from 'Components/Page/PageContent';
import PageContentBodyConnector from 'Components/Page/PageContentBodyConnector'; import PageContentBodyConnector from 'Components/Page/PageContentBodyConnector';
@ -15,6 +15,7 @@ import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator'; import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection'; import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton'; import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
import ConfirmModal from 'Components/Modal/ConfirmModal';
import NoMovie from 'Movie/NoMovie'; import NoMovie from 'Movie/NoMovie';
import MovieIndexTableConnector from './Table/MovieIndexTableConnector'; import MovieIndexTableConnector from './Table/MovieIndexTableConnector';
import MovieIndexTableOptionsConnector from './Table/MovieIndexTableOptionsConnector'; import MovieIndexTableOptionsConnector from './Table/MovieIndexTableOptionsConnector';
@ -24,6 +25,7 @@ import MovieIndexOverviewOptionsModal from './Overview/Options/MovieIndexOvervie
import MovieIndexOverviewsConnector from './Overview/MovieIndexOverviewsConnector'; import MovieIndexOverviewsConnector from './Overview/MovieIndexOverviewsConnector';
import MovieIndexFilterMenu from './Menus/MovieIndexFilterMenu'; import MovieIndexFilterMenu from './Menus/MovieIndexFilterMenu';
import MovieIndexSortMenu from './Menus/MovieIndexSortMenu'; import MovieIndexSortMenu from './Menus/MovieIndexSortMenu';
import MovieIndexSearchMenu from './Menus/MovieIndexSearchMenu';
import MovieIndexViewMenu from './Menus/MovieIndexViewMenu'; import MovieIndexViewMenu from './Menus/MovieIndexViewMenu';
import MovieIndexFooterConnector from './MovieIndexFooterConnector'; import MovieIndexFooterConnector from './MovieIndexFooterConnector';
import MovieEditorFooter from 'Movie/Editor/MovieEditorFooter.js'; import MovieEditorFooter from 'Movie/Editor/MovieEditorFooter.js';
@ -60,6 +62,8 @@ class MovieIndex extends Component {
isInteractiveImportModalOpen: false, isInteractiveImportModalOpen: false,
isMovieEditorActive: false, isMovieEditorActive: false,
isOrganizingMovieModalOpen: false, isOrganizingMovieModalOpen: false,
isConfirmSearchModalOpen: false,
searchType: null,
allSelected: false, allSelected: false,
allUnselected: false, allUnselected: false,
lastToggled: null, lastToggled: null,
@ -215,7 +219,7 @@ class MovieIndex extends Component {
if (this.state.isMovieEditorActive) { if (this.state.isMovieEditorActive) {
this.setState({ isMovieEditorActive: false }); this.setState({ isMovieEditorActive: false });
} else { } else {
const newState = selectAll(this.state.selectedState, false) const newState = selectAll(this.state.selectedState, false);
newState.isMovieEditorActive = true; newState.isMovieEditorActive = true;
this.setState(newState); this.setState(newState);
} }
@ -258,6 +262,19 @@ class MovieIndex extends Component {
} }
} }
onSearchPress = (command) => {
this.setState({ isConfirmSearchModalOpen: true, searchType: command });
}
onSearchConfirmed = () => {
this.props.onSearchPress(this.state.searchType);
this.setState({ isConfirmSearchModalOpen: false });
}
onConfirmSearchModalClose = () => {
this.setState({ isConfirmSearchModalOpen: false });
}
onRender = () => { onRender = () => {
this.setState({ isRendered: true }, () => { this.setState({ isRendered: true }, () => {
const { const {
@ -299,6 +316,7 @@ class MovieIndex extends Component {
isRefreshingMovie, isRefreshingMovie,
isRssSyncExecuting, isRssSyncExecuting,
isOrganizingMovie, isOrganizingMovie,
isSearchingMovies,
isSaving, isSaving,
saveError, saveError,
isDeleting, isDeleting,
@ -309,6 +327,7 @@ class MovieIndex extends Component {
onViewSelect, onViewSelect,
onRefreshMoviePress, onRefreshMoviePress,
onRssSyncPress, onRssSyncPress,
onSearchPress,
...otherProps ...otherProps
} = this.props; } = this.props;
@ -319,6 +338,7 @@ class MovieIndex extends Component {
isPosterOptionsModalOpen, isPosterOptionsModalOpen,
isOverviewOptionsModalOpen, isOverviewOptionsModalOpen,
isInteractiveImportModalOpen, isInteractiveImportModalOpen,
isConfirmSearchModalOpen,
isMovieEditorActive, isMovieEditorActive,
isRendered, isRendered,
selectedState, selectedState,
@ -355,10 +375,9 @@ class MovieIndex extends Component {
<PageToolbarSeparator /> <PageToolbarSeparator />
<PageToolbarButton <MovieIndexSearchMenu
label="Search Missing" isDisabled={isSearchingMovies}
iconName={icons.SEARCH} onSearchPress={this.onSearchPress}
isDisabled={hasNoMovie}
/> />
<PageToolbarButton <PageToolbarButton
@ -564,6 +583,25 @@ class MovieIndex extends Component {
movieIds={selectedMovieIds} movieIds={selectedMovieIds}
onModalClose={this.onOrganizeMovieModalClose} onModalClose={this.onOrganizeMovieModalClose}
/> />
<ConfirmModal
isOpen={isConfirmSearchModalOpen}
kind={kinds.DANGER}
title="Mass Movie Search"
message={
<div>
<div>
Are you sure you want to perform mass movie search?
</div>
<div>
This cannot be cancelled once started without restarting Radarr.
</div>
</div>
}
confirmLabel="Search"
onConfirm={this.onSearchConfirmed}
onCancel={this.onConfirmSearchModalClose}
/>
</PageContent> </PageContent>
); );
} }
@ -584,6 +622,7 @@ MovieIndex.propTypes = {
view: PropTypes.string.isRequired, view: PropTypes.string.isRequired,
isRefreshingMovie: PropTypes.bool.isRequired, isRefreshingMovie: PropTypes.bool.isRequired,
isOrganizingMovie: PropTypes.bool.isRequired, isOrganizingMovie: PropTypes.bool.isRequired,
isSearchingMovies: PropTypes.bool.isRequired,
isRssSyncExecuting: PropTypes.bool.isRequired, isRssSyncExecuting: PropTypes.bool.isRequired,
scrollTop: PropTypes.number.isRequired, scrollTop: PropTypes.number.isRequired,
isSmallScreen: PropTypes.bool.isRequired, isSmallScreen: PropTypes.bool.isRequired,
@ -596,6 +635,7 @@ MovieIndex.propTypes = {
onViewSelect: PropTypes.func.isRequired, onViewSelect: PropTypes.func.isRequired,
onRefreshMoviePress: PropTypes.func.isRequired, onRefreshMoviePress: PropTypes.func.isRequired,
onRssSyncPress: PropTypes.func.isRequired, onRssSyncPress: PropTypes.func.isRequired,
onSearchPress: PropTypes.func.isRequired,
onScroll: PropTypes.func.isRequired, onScroll: PropTypes.func.isRequired,
onSaveSelected: PropTypes.func.isRequired onSaveSelected: PropTypes.func.isRequired
}; };

View File

@ -43,12 +43,16 @@ function createMapStateToProps() {
createCommandExecutingSelector(commandNames.REFRESH_MOVIE), createCommandExecutingSelector(commandNames.REFRESH_MOVIE),
createCommandExecutingSelector(commandNames.RSS_SYNC), createCommandExecutingSelector(commandNames.RSS_SYNC),
createCommandExecutingSelector(commandNames.RENAME_MOVIE), createCommandExecutingSelector(commandNames.RENAME_MOVIE),
createCommandExecutingSelector(commandNames.CUTOFF_UNMET_MOVIES_SEARCH),
createCommandExecutingSelector(commandNames.MISSING_MOVIES_SEARCH),
createDimensionsSelector(), createDimensionsSelector(),
( (
movies, movies,
isRefreshingMovie, isRefreshingMovie,
isRssSyncExecuting, isRssSyncExecuting,
isOrganizingMovie, isOrganizingMovie,
isCutoffMoviesSearch,
isMissingMoviesSearch,
dimensionsState dimensionsState
) => { ) => {
return { return {
@ -56,6 +60,7 @@ function createMapStateToProps() {
isRefreshingMovie, isRefreshingMovie,
isRssSyncExecuting, isRssSyncExecuting,
isOrganizingMovie, isOrganizingMovie,
isSearchingMovies: isCutoffMoviesSearch || isMissingMoviesSearch,
isSmallScreen: dimensionsState.isSmallScreen isSmallScreen: dimensionsState.isSmallScreen
}; };
} }
@ -98,6 +103,12 @@ function createMapDispatchToProps(dispatch, props) {
dispatch(executeCommand({ dispatch(executeCommand({
name: commandNames.RSS_SYNC name: commandNames.RSS_SYNC
})); }));
},
onSearchPress(command) {
dispatch(executeCommand({
name: command
}));
} }
}; };
} }

View File

@ -90,7 +90,6 @@ public void Execute(CutoffUnmetMoviesSearchCommand message)
List<Movie> movies = _movieCutoffService.MoviesWhereCutoffUnmet(pagingSpec).Records.ToList(); List<Movie> movies = _movieCutoffService.MoviesWhereCutoffUnmet(pagingSpec).Records.ToList();
var queue = _queueService.GetQueue().Select(q => q.Movie.Id); var queue = _queueService.GetQueue().Select(q => q.Movie.Id);
var missing = movies.Where(e => !queue.Contains(e.Id)).ToList(); var missing = movies.Where(e => !queue.Contains(e.Id)).ToList();

View File

@ -177,7 +177,9 @@ public PagingSpec<Movie> MoviesWhereCutoffUnmet(PagingSpec<Movie> pagingSpec, Li
private SortBuilder<Movie> MoviesWhereCutoffUnmetQuery(PagingSpec<Movie> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff) private SortBuilder<Movie> MoviesWhereCutoffUnmetQuery(PagingSpec<Movie> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff)
{ {
return Query.Where(pagingSpec.FilterExpressions.FirstOrDefault()) return Query
.Join<Movie, MovieFile>(JoinType.Left, e => e.MovieFile, (e, s) => e.MovieFileId == s.Id)
.Where(pagingSpec.FilterExpressions.FirstOrDefault())
.AndWhere(m => m.MovieFileId != 0) .AndWhere(m => m.MovieFileId != 0)
.AndWhere(BuildQualityCutoffWhereClause(qualitiesBelowCutoff)) .AndWhere(BuildQualityCutoffWhereClause(qualitiesBelowCutoff))
.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection()) .OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection())