mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2025-09-17 17:14:18 +02:00
Fixed: Wanted/Cutoff Search from Index Page
This commit is contained in:
@@ -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';
|
||||||
|
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 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
|
||||||
};
|
};
|
||||||
|
@@ -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
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -90,7 +90,6 @@ namespace NzbDrone.Core.IndexerSearch
|
|||||||
|
|
||||||
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();
|
||||||
|
|
||||||
|
@@ -177,7 +177,9 @@ namespace NzbDrone.Core.Movies
|
|||||||
|
|
||||||
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())
|
||||||
|
Reference in New Issue
Block a user