mirror of
https://github.com/Radarr/Radarr
synced 2024-12-29 03:15:19 +00:00
Fixed: Wanted/Cutoff Search from Index Page
This commit is contained in:
parent
6705b59b23
commit
d263ff9a6b
7 changed files with 161 additions and 10 deletions
|
@ -3,12 +3,12 @@ export const BACKUP = 'Backup';
|
|||
export const CHECK_FOR_FINISHED_DOWNLOAD = 'CheckForFinishedDownload';
|
||||
export const CLEAR_BLACKLIST = 'ClearBlacklist';
|
||||
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_UPDATE_LOG_FILES = 'DeleteUpdateLogFiles';
|
||||
export const DOWNLOADED_EPSIODES_SCAN = 'DownloadedEpisodesScan';
|
||||
export const INTERACTIVE_IMPORT = 'ManualImport';
|
||||
export const MISSING_EPISODE_SEARCH = 'MissingEpisodeSearch';
|
||||
export const MISSING_MOVIES_SEARCH = 'MissingMoviesSearch';
|
||||
export const MOVE_MOVIE = 'MoveMovie';
|
||||
export const REFRESH_MOVIE = 'RefreshMovie';
|
||||
export const RENAME_FILES = 'RenameFiles';
|
||||
|
|
47
frontend/src/Components/Menu/SearchMenuItem.js
Normal file
47
frontend/src/Components/Menu/SearchMenuItem.js
Normal 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;
|
52
frontend/src/Movie/Index/Menus/MovieIndexSearchMenu.js
Normal file
52
frontend/src/Movie/Index/Menus/MovieIndexSearchMenu.js
Normal 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;
|
|
@ -5,7 +5,7 @@ import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
|||
import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
||||
import selectAll from 'Utilities/Table/selectAll';
|
||||
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 PageContent from 'Components/Page/PageContent';
|
||||
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 PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
|
||||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||
import NoMovie from 'Movie/NoMovie';
|
||||
import MovieIndexTableConnector from './Table/MovieIndexTableConnector';
|
||||
import MovieIndexTableOptionsConnector from './Table/MovieIndexTableOptionsConnector';
|
||||
|
@ -24,6 +25,7 @@ import MovieIndexOverviewOptionsModal from './Overview/Options/MovieIndexOvervie
|
|||
import MovieIndexOverviewsConnector from './Overview/MovieIndexOverviewsConnector';
|
||||
import MovieIndexFilterMenu from './Menus/MovieIndexFilterMenu';
|
||||
import MovieIndexSortMenu from './Menus/MovieIndexSortMenu';
|
||||
import MovieIndexSearchMenu from './Menus/MovieIndexSearchMenu';
|
||||
import MovieIndexViewMenu from './Menus/MovieIndexViewMenu';
|
||||
import MovieIndexFooterConnector from './MovieIndexFooterConnector';
|
||||
import MovieEditorFooter from 'Movie/Editor/MovieEditorFooter.js';
|
||||
|
@ -60,6 +62,8 @@ class MovieIndex extends Component {
|
|||
isInteractiveImportModalOpen: false,
|
||||
isMovieEditorActive: false,
|
||||
isOrganizingMovieModalOpen: false,
|
||||
isConfirmSearchModalOpen: false,
|
||||
searchType: null,
|
||||
allSelected: false,
|
||||
allUnselected: false,
|
||||
lastToggled: null,
|
||||
|
@ -215,7 +219,7 @@ class MovieIndex extends Component {
|
|||
if (this.state.isMovieEditorActive) {
|
||||
this.setState({ isMovieEditorActive: false });
|
||||
} else {
|
||||
const newState = selectAll(this.state.selectedState, false)
|
||||
const newState = selectAll(this.state.selectedState, false);
|
||||
newState.isMovieEditorActive = true;
|
||||
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 = () => {
|
||||
this.setState({ isRendered: true }, () => {
|
||||
const {
|
||||
|
@ -299,6 +316,7 @@ class MovieIndex extends Component {
|
|||
isRefreshingMovie,
|
||||
isRssSyncExecuting,
|
||||
isOrganizingMovie,
|
||||
isSearchingMovies,
|
||||
isSaving,
|
||||
saveError,
|
||||
isDeleting,
|
||||
|
@ -309,6 +327,7 @@ class MovieIndex extends Component {
|
|||
onViewSelect,
|
||||
onRefreshMoviePress,
|
||||
onRssSyncPress,
|
||||
onSearchPress,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
|
@ -319,6 +338,7 @@ class MovieIndex extends Component {
|
|||
isPosterOptionsModalOpen,
|
||||
isOverviewOptionsModalOpen,
|
||||
isInteractiveImportModalOpen,
|
||||
isConfirmSearchModalOpen,
|
||||
isMovieEditorActive,
|
||||
isRendered,
|
||||
selectedState,
|
||||
|
@ -355,10 +375,9 @@ class MovieIndex extends Component {
|
|||
|
||||
<PageToolbarSeparator />
|
||||
|
||||
<PageToolbarButton
|
||||
label="Search Missing"
|
||||
iconName={icons.SEARCH}
|
||||
isDisabled={hasNoMovie}
|
||||
<MovieIndexSearchMenu
|
||||
isDisabled={isSearchingMovies}
|
||||
onSearchPress={this.onSearchPress}
|
||||
/>
|
||||
|
||||
<PageToolbarButton
|
||||
|
@ -564,6 +583,25 @@ class MovieIndex extends Component {
|
|||
movieIds={selectedMovieIds}
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
@ -584,6 +622,7 @@ MovieIndex.propTypes = {
|
|||
view: PropTypes.string.isRequired,
|
||||
isRefreshingMovie: PropTypes.bool.isRequired,
|
||||
isOrganizingMovie: PropTypes.bool.isRequired,
|
||||
isSearchingMovies: PropTypes.bool.isRequired,
|
||||
isRssSyncExecuting: PropTypes.bool.isRequired,
|
||||
scrollTop: PropTypes.number.isRequired,
|
||||
isSmallScreen: PropTypes.bool.isRequired,
|
||||
|
@ -596,6 +635,7 @@ MovieIndex.propTypes = {
|
|||
onViewSelect: PropTypes.func.isRequired,
|
||||
onRefreshMoviePress: PropTypes.func.isRequired,
|
||||
onRssSyncPress: PropTypes.func.isRequired,
|
||||
onSearchPress: PropTypes.func.isRequired,
|
||||
onScroll: PropTypes.func.isRequired,
|
||||
onSaveSelected: PropTypes.func.isRequired
|
||||
};
|
||||
|
|
|
@ -43,12 +43,16 @@ function createMapStateToProps() {
|
|||
createCommandExecutingSelector(commandNames.REFRESH_MOVIE),
|
||||
createCommandExecutingSelector(commandNames.RSS_SYNC),
|
||||
createCommandExecutingSelector(commandNames.RENAME_MOVIE),
|
||||
createCommandExecutingSelector(commandNames.CUTOFF_UNMET_MOVIES_SEARCH),
|
||||
createCommandExecutingSelector(commandNames.MISSING_MOVIES_SEARCH),
|
||||
createDimensionsSelector(),
|
||||
(
|
||||
movies,
|
||||
isRefreshingMovie,
|
||||
isRssSyncExecuting,
|
||||
isOrganizingMovie,
|
||||
isCutoffMoviesSearch,
|
||||
isMissingMoviesSearch,
|
||||
dimensionsState
|
||||
) => {
|
||||
return {
|
||||
|
@ -56,6 +60,7 @@ function createMapStateToProps() {
|
|||
isRefreshingMovie,
|
||||
isRssSyncExecuting,
|
||||
isOrganizingMovie,
|
||||
isSearchingMovies: isCutoffMoviesSearch || isMissingMoviesSearch,
|
||||
isSmallScreen: dimensionsState.isSmallScreen
|
||||
};
|
||||
}
|
||||
|
@ -98,6 +103,12 @@ function createMapDispatchToProps(dispatch, props) {
|
|||
dispatch(executeCommand({
|
||||
name: commandNames.RSS_SYNC
|
||||
}));
|
||||
},
|
||||
|
||||
onSearchPress(command) {
|
||||
dispatch(executeCommand({
|
||||
name: command
|
||||
}));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -90,7 +90,6 @@ public void Execute(CutoffUnmetMoviesSearchCommand message)
|
|||
|
||||
List<Movie> movies = _movieCutoffService.MoviesWhereCutoffUnmet(pagingSpec).Records.ToList();
|
||||
|
||||
|
||||
var queue = _queueService.GetQueue().Select(q => q.Movie.Id);
|
||||
var missing = movies.Where(e => !queue.Contains(e.Id)).ToList();
|
||||
|
||||
|
|
|
@ -177,7 +177,9 @@ public PagingSpec<Movie> MoviesWhereCutoffUnmet(PagingSpec<Movie> pagingSpec, Li
|
|||
|
||||
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(BuildQualityCutoffWhereClause(qualitiesBelowCutoff))
|
||||
.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection())
|
||||
|
|
Loading…
Reference in a new issue