+
+
{
columns.map((column) => {
const {
@@ -100,7 +111,10 @@ class AddListMovieHeader extends Component {
AddListMovieHeader.propTypes = {
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
- onTableOptionChange: PropTypes.func.isRequired
+ onTableOptionChange: PropTypes.func.isRequired,
+ allSelected: PropTypes.bool.isRequired,
+ allUnselected: PropTypes.bool.isRequired,
+ onSelectAllChange: PropTypes.func.isRequired
};
export default AddListMovieHeader;
diff --git a/frontend/src/AddMovie/AddListMovie/Table/AddListMovieHeaderConnector.js b/frontend/src/DiscoverMovie/Table/AddListMovieHeaderConnector.js
similarity index 81%
rename from frontend/src/AddMovie/AddListMovie/Table/AddListMovieHeaderConnector.js
rename to frontend/src/DiscoverMovie/Table/AddListMovieHeaderConnector.js
index 64109721f..f4b9551c8 100644
--- a/frontend/src/AddMovie/AddListMovie/Table/AddListMovieHeaderConnector.js
+++ b/frontend/src/DiscoverMovie/Table/AddListMovieHeaderConnector.js
@@ -1,5 +1,5 @@
import { connect } from 'react-redux';
-import { setListMovieTableOption } from 'Store/Actions/addMovieActions';
+import { setListMovieTableOption } from 'Store/Actions/discoverMovieActions';
import AddListMovieHeader from './AddListMovieHeader';
function createMapDispatchToProps(dispatch, props) {
diff --git a/frontend/src/DiscoverMovie/Table/AddListMovieRow.css b/frontend/src/DiscoverMovie/Table/AddListMovieRow.css
new file mode 100644
index 000000000..912c6ce4d
--- /dev/null
+++ b/frontend/src/DiscoverMovie/Table/AddListMovieRow.css
@@ -0,0 +1,65 @@
+.cell {
+ composes: cell from '~Components/Table/Cells/VirtualTableRowCell.css';
+
+ display: flex;
+ align-items: center;
+}
+
+.status {
+ composes: cell;
+
+ flex: 0 0 60px;
+}
+
+.collection,
+.sortTitle {
+ composes: cell;
+
+ flex: 4 0 110px;
+}
+
+.studio {
+ composes: cell;
+
+ flex: 2 0 90px;
+}
+
+.inCinemas,
+.physicalRelease,
+.genres {
+ composes: cell;
+
+ flex: 0 0 180px;
+}
+
+.movieStatus,
+.certification {
+ composes: cell;
+
+ flex: 0 0 100px;
+}
+
+.ratings {
+ composes: cell;
+
+ flex: 0 0 80px;
+}
+
+.tags {
+ composes: cell;
+
+ flex: 1 0 60px;
+}
+
+.actions {
+ composes: cell;
+
+ flex: 0 1 90px;
+ min-width: 60px;
+}
+
+.checkInput {
+ composes: input from '~Components/Form/CheckInput.css';
+
+ margin-top: 0;
+}
diff --git a/frontend/src/AddMovie/AddListMovie/Table/AddListMovieRow.js b/frontend/src/DiscoverMovie/Table/AddListMovieRow.js
similarity index 69%
rename from frontend/src/AddMovie/AddListMovie/Table/AddListMovieRow.js
rename to frontend/src/DiscoverMovie/Table/AddListMovieRow.js
index 2a96225cc..d8e32f607 100644
--- a/frontend/src/AddMovie/AddListMovie/Table/AddListMovieRow.js
+++ b/frontend/src/DiscoverMovie/Table/AddListMovieRow.js
@@ -1,11 +1,15 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
+import { icons } from 'Helpers/Props';
import HeartRating from 'Components/HeartRating';
+import IconButton from 'Components/Link/IconButton';
import VirtualTableRowCell from 'Components/Table/Cells/VirtualTableRowCell';
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
-import MovieStatusCell from './MovieStatusCell';
+import ListMovieStatusCell from './ListMovieStatusCell';
import Link from 'Components/Link/Link';
-import AddNewMovieModal from 'AddMovie/AddNewMovie/AddNewMovieModal';
+import AddNewDiscoverMovieModal from 'DiscoverMovie/AddNewDiscoverMovieModal';
+import ExcludeMovieModal from 'DiscoverMovie/Exclusion/ExcludeMovieModal';
+import VirtualTableSelectCell from 'Components/Table/Cells/VirtualTableSelectCell';
import styles from './AddListMovieRow.css';
class AddListMovieRow extends Component {
@@ -17,14 +21,15 @@ class AddListMovieRow extends Component {
super(props, context);
this.state = {
- isNewAddMovieModalOpen: false
+ isNewAddMovieModalOpen: false,
+ isExcludeMovieModalOpen: false
};
}
//
// Listeners
- onPress = () => {
+ onAddMoviePress = () => {
this.setState({ isNewAddMovieModalOpen: true });
}
@@ -32,6 +37,14 @@ class AddListMovieRow extends Component {
this.setState({ isNewAddMovieModalOpen: false });
}
+ onExcludeMoviePress = () => {
+ this.setState({ isExcludeMovieModalOpen: true });
+ }
+
+ onExcludeMovieModalClose = () => {
+ this.setState({ isExcludeMovieModalOpen: false });
+ }
+
//
// Render
@@ -52,17 +65,30 @@ class AddListMovieRow extends Component {
ratings,
certification,
columns,
- isExistingMovie
+ isExisting,
+ isExcluded,
+ isSelected,
+ onSelectedChange
} = this.props;
const {
- isNewAddMovieModalOpen
+ isNewAddMovieModalOpen,
+ isExcludeMovieModalOpen
} = this.state;
- const linkProps = isExistingMovie ? { to: `/movie/${titleSlug}` } : { onPress: this.onPress };
+ const linkProps = isExisting ? { to: `/movie/${titleSlug}` } : { onPress: this.onAddMoviePress };
return (
<>
+
+
{
columns.map((column) => {
const {
@@ -76,10 +102,11 @@ class AddListMovieRow extends Component {
if (name === 'status') {
return (
-
);
@@ -172,12 +199,28 @@ class AddListMovieRow extends Component {
);
}
+ if (name === 'actions') {
+ return (
+
+
+
+ );
+ }
+
return null;
})
}
-
+
+
>
);
}
@@ -207,7 +258,10 @@ AddListMovieRow.propTypes = {
ratings: PropTypes.object.isRequired,
certification: PropTypes.string,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
- isExistingMovie: PropTypes.bool.isRequired
+ isExisting: PropTypes.bool.isRequired,
+ isExcluded: PropTypes.bool.isRequired,
+ isSelected: PropTypes.bool,
+ onSelectedChange: PropTypes.func.isRequired
};
AddListMovieRow.defaultProps = {
diff --git a/frontend/src/AddMovie/AddListMovie/Table/AddListMovieRowConnector.js b/frontend/src/DiscoverMovie/Table/AddListMovieRowConnector.js
similarity index 56%
rename from frontend/src/AddMovie/AddListMovie/Table/AddListMovieRowConnector.js
rename to frontend/src/DiscoverMovie/Table/AddListMovieRowConnector.js
index 684b7efc9..b00c42737 100644
--- a/frontend/src/AddMovie/AddListMovie/Table/AddListMovieRowConnector.js
+++ b/frontend/src/DiscoverMovie/Table/AddListMovieRowConnector.js
@@ -1,19 +1,13 @@
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
-import createExistingMovieSelector from 'Store/Selectors/createExistingMovieSelector';
-import createExclusionMovieSelector from 'Store/Selectors/createExclusionMovieSelector';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
import AddListMovieRow from './AddListMovieRow';
function createMapStateToProps() {
return createSelector(
- createExistingMovieSelector(),
- createExclusionMovieSelector(),
createDimensionsSelector(),
- (isExistingMovie, isExclusionMovie, dimensions) => {
+ (dimensions) => {
return {
- isExistingMovie,
- isExclusionMovie,
isSmallScreen: dimensions.isSmallScreen
};
}
diff --git a/frontend/src/AddMovie/AddListMovie/Table/AddListMovieTable.css b/frontend/src/DiscoverMovie/Table/AddListMovieTable.css
similarity index 100%
rename from frontend/src/AddMovie/AddListMovie/Table/AddListMovieTable.css
rename to frontend/src/DiscoverMovie/Table/AddListMovieTable.css
diff --git a/frontend/src/AddMovie/AddListMovie/Table/AddListMovieTable.js b/frontend/src/DiscoverMovie/Table/AddListMovieTable.js
similarity index 74%
rename from frontend/src/AddMovie/AddListMovie/Table/AddListMovieTable.js
rename to frontend/src/DiscoverMovie/Table/AddListMovieTable.js
index 8d8faf42e..5278ec69a 100644
--- a/frontend/src/AddMovie/AddListMovie/Table/AddListMovieTable.js
+++ b/frontend/src/DiscoverMovie/Table/AddListMovieTable.js
@@ -4,7 +4,7 @@ import getIndexOfFirstCharacter from 'Utilities/Array/getIndexOfFirstCharacter';
import { sortDirections } from 'Helpers/Props';
import VirtualTable from 'Components/Table/VirtualTable';
import VirtualTableRow from 'Components/Table/VirtualTableRow';
-import AddListMovieItemConnector from 'AddMovie/AddListMovie/AddListMovieItemConnector';
+import AddListMovieItemConnector from 'DiscoverMovie/AddListMovieItemConnector';
import AddListMovieHeaderConnector from './AddListMovieHeaderConnector';
import AddListMovieRowConnector from './AddListMovieRowConnector';
import styles from './AddListMovieTable.css';
@@ -46,7 +46,9 @@ class AddListMovieTable extends Component {
rowRenderer = ({ key, rowIndex, style }) => {
const {
items,
- columns
+ columns,
+ selectedState,
+ onSelectedChange
} = this.props;
const movie = items[rowIndex];
@@ -57,10 +59,12 @@ class AddListMovieTable extends Component {
style={style}
>
);
@@ -75,8 +79,13 @@ class AddListMovieTable extends Component {
columns,
sortKey,
sortDirection,
+ isSmallScreen,
+ onSortPress,
scroller,
- onSortPress
+ allSelected,
+ allUnselected,
+ onSelectAllChange,
+ selectedState
} = this.props;
return (
@@ -84,6 +93,7 @@ class AddListMovieTable extends Component {
className={styles.tableContainer}
items={items}
scrollIndex={this.state.scrollIndex}
+ isSmallScreen={isSmallScreen}
scroller={scroller}
rowHeight={38}
overscanRowCount={2}
@@ -94,8 +104,12 @@ class AddListMovieTable extends Component {
sortKey={sortKey}
sortDirection={sortDirection}
onSortPress={onSortPress}
+ allSelected={allSelected}
+ allUnselected={allUnselected}
+ onSelectAllChange={onSelectAllChange}
/>
}
+ selectedState={selectedState}
columns={columns}
/>
);
@@ -108,8 +122,14 @@ AddListMovieTable.propTypes = {
sortKey: PropTypes.string,
sortDirection: PropTypes.oneOf(sortDirections.all),
jumpToCharacter: PropTypes.string,
+ isSmallScreen: PropTypes.bool.isRequired,
scroller: PropTypes.instanceOf(Element).isRequired,
- onSortPress: PropTypes.func.isRequired
+ onSortPress: PropTypes.func.isRequired,
+ allSelected: PropTypes.bool.isRequired,
+ allUnselected: PropTypes.bool.isRequired,
+ selectedState: PropTypes.object.isRequired,
+ onSelectedChange: PropTypes.func.isRequired,
+ onSelectAllChange: PropTypes.func.isRequired
};
export default AddListMovieTable;
diff --git a/frontend/src/AddMovie/AddListMovie/Table/AddListMovieTableConnector.js b/frontend/src/DiscoverMovie/Table/AddListMovieTableConnector.js
similarity index 84%
rename from frontend/src/AddMovie/AddListMovie/Table/AddListMovieTableConnector.js
rename to frontend/src/DiscoverMovie/Table/AddListMovieTableConnector.js
index f0988c2d9..f1ca887a6 100644
--- a/frontend/src/AddMovie/AddListMovie/Table/AddListMovieTableConnector.js
+++ b/frontend/src/DiscoverMovie/Table/AddListMovieTableConnector.js
@@ -1,12 +1,12 @@
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
-import { setListMovieSort } from 'Store/Actions/addMovieActions';
+import { setListMovieSort } from 'Store/Actions/discoverMovieActions';
import AddListMovieTable from './AddListMovieTable';
function createMapStateToProps() {
return createSelector(
(state) => state.app.dimensions,
- (state) => state.addMovie.columns,
+ (state) => state.discoverMovie.columns,
(dimensions, columns) => {
return {
isSmallScreen: dimensions.isSmallScreen,
diff --git a/frontend/src/AddMovie/AddListMovie/Table/MovieStatusCell.css b/frontend/src/DiscoverMovie/Table/ListMovieStatusCell.css
similarity index 76%
rename from frontend/src/AddMovie/AddListMovie/Table/MovieStatusCell.css
rename to frontend/src/DiscoverMovie/Table/ListMovieStatusCell.css
index fbcd5eee9..441cd6669 100644
--- a/frontend/src/AddMovie/AddListMovie/Table/MovieStatusCell.css
+++ b/frontend/src/DiscoverMovie/Table/ListMovieStatusCell.css
@@ -7,3 +7,7 @@
.statusIcon {
width: 20px !important;
}
+
+.exclusionIcon {
+ color: $dangerColor;
+}
diff --git a/frontend/src/DiscoverMovie/Table/ListMovieStatusCell.js b/frontend/src/DiscoverMovie/Table/ListMovieStatusCell.js
new file mode 100644
index 000000000..f161b7424
--- /dev/null
+++ b/frontend/src/DiscoverMovie/Table/ListMovieStatusCell.js
@@ -0,0 +1,56 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import { icons } from 'Helpers/Props';
+import Icon from 'Components/Icon';
+import VirtualTableRowCell from 'Components/Table/Cells/TableRowCell';
+import { getMovieStatusDetails } from 'Movie/MovieStatus';
+import styles from './ListMovieStatusCell.css';
+
+function ListMovieStatusCell(props) {
+ const {
+ className,
+ status,
+ isExclusion,
+ component: Component,
+ ...otherProps
+ } = props;
+
+ const statusDetails = getMovieStatusDetails(status);
+
+ return (
+
+
+
+ {
+ isExclusion ?
+ : null
+ }
+
+
+ );
+}
+
+ListMovieStatusCell.propTypes = {
+ className: PropTypes.string.isRequired,
+ status: PropTypes.string.isRequired,
+ isExclusion: PropTypes.bool.isRequired,
+ component: PropTypes.elementType
+};
+
+ListMovieStatusCell.defaultProps = {
+ className: styles.status,
+ component: VirtualTableRowCell
+};
+
+export default ListMovieStatusCell;
diff --git a/frontend/src/Movie/Index/Posters/MovieIndexPoster.css b/frontend/src/Movie/Index/Posters/MovieIndexPoster.css
index 65f34c4c8..7dddf9b37 100644
--- a/frontend/src/Movie/Index/Posters/MovieIndexPoster.css
+++ b/frontend/src/Movie/Index/Posters/MovieIndexPoster.css
@@ -60,6 +60,7 @@ $hoverScale: 1.05;
position: absolute;
top: 0;
right: 0;
+ z-index: 1;
width: 0;
height: 0;
border-width: 0 25px 25px 0;
diff --git a/frontend/src/Movie/Index/Posters/MovieIndexPosters.css b/frontend/src/Movie/Index/Posters/MovieIndexPosters.css
index d80f951a0..9c6520fb5 100644
--- a/frontend/src/Movie/Index/Posters/MovieIndexPosters.css
+++ b/frontend/src/Movie/Index/Posters/MovieIndexPosters.css
@@ -1,7 +1,3 @@
.grid {
flex: 1 0 auto;
}
-
-.container {
- padding: 10px;
-}
diff --git a/frontend/src/Movie/Index/Posters/MovieIndexPosters.js b/frontend/src/Movie/Index/Posters/MovieIndexPosters.js
index 5f7eddb8e..606a298f1 100644
--- a/frontend/src/Movie/Index/Posters/MovieIndexPosters.js
+++ b/frontend/src/Movie/Index/Posters/MovieIndexPosters.js
@@ -104,6 +104,7 @@ class MovieIndexPosters extends Component {
this._isInitialized = false;
this._grid = null;
+ this._padding = props.isSmallScreen ? columnPaddingSmallScreen : columnPadding;
}
componentDidUpdate(prevProps, prevState) {
@@ -112,6 +113,7 @@ class MovieIndexPosters extends Component {
sortKey,
posterOptions,
jumpToCharacter,
+ isSmallScreen,
isMovieEditorActive
} = this.props;
@@ -124,7 +126,7 @@ class MovieIndexPosters extends Component {
if (prevProps.sortKey !== sortKey ||
prevProps.posterOptions !== posterOptions) {
- this.calculateGrid();
+ this.calculateGrid(width, isSmallScreen);
}
if (this._grid &&
@@ -165,10 +167,9 @@ class MovieIndexPosters extends Component {
posterOptions
} = this.props;
- const padding = isSmallScreen ? columnPaddingSmallScreen : columnPadding;
const columnWidth = calculateColumnWidth(width, posterOptions.size, isSmallScreen);
const columnCount = Math.max(Math.floor(width / columnWidth), 1);
- const posterWidth = columnWidth - padding;
+ const posterWidth = columnWidth - this._padding * 2;
const posterHeight = calculatePosterHeight(posterWidth);
const rowHeight = calculateRowHeight(posterHeight, sortKey, isSmallScreen, posterOptions);
@@ -219,7 +220,10 @@ class MovieIndexPosters extends Component {
{
- acc.push({
- id: movie.studio,
- name: movie.studio
- });
-
- return acc;
- }, []);
-
- return tagList.sort(sortByName);
- }
- },
- {
- name: 'inCinemas',
- label: 'In Cinemas',
- type: filterBuilderTypes.DATE,
- valueType: filterBuilderValueTypes.DATE
- },
- {
- name: 'physicalRelease',
- label: 'Physical Release',
- type: filterBuilderTypes.DATE,
- valueType: filterBuilderValueTypes.DATE
- },
- {
- name: 'genres',
- label: 'Genres',
- type: filterBuilderTypes.ARRAY,
- optionsSelector: function(items) {
- const tagList = items.reduce((acc, movie) => {
- movie.genres.forEach((genre) => {
- acc.push({
- id: genre,
- name: genre
- });
- });
-
- return acc;
- }, []);
-
- return tagList.sort(sortByName);
- }
- },
- {
- name: 'ratings',
- label: 'Rating',
- type: filterBuilderTypes.NUMBER
- },
- {
- name: 'certification',
- label: 'Certification',
- type: filterBuilderTypes.EXACT
- },
- {
- name: 'tags',
- label: 'Tags',
- type: filterBuilderTypes.ARRAY,
- valueType: filterBuilderValueTypes.TAG
- }
- ]
+ }
};
export const persistState = [
- 'addMovie.defaults',
- 'addMovie.sortKey',
- 'addMovie.sortDirection',
- 'addMovie.selectedFilterKey',
- 'addMovie.customFilters',
- 'addMovie.view',
- 'addMovie.columns',
- 'addMovie.posterOptions',
- 'addMovie.overviewOptions',
- 'addMovie.tableOptions'
+ 'addMovie.defaults'
];
//
@@ -254,16 +50,6 @@ export const SET_ADD_MOVIE_VALUE = 'addMovie/setAddMovieValue';
export const CLEAR_ADD_MOVIE = 'addMovie/clearAddMovie';
export const SET_ADD_MOVIE_DEFAULT = 'addMovie/setAddMovieDefault';
-export const FETCH_LIST_MOVIES = 'addMovie/fetchListMovies';
-export const FETCH_DISCOVER_MOVIES = 'addMovie/fetchDiscoverMovies';
-
-export const SET_LIST_MOVIE_SORT = 'addMovie/setListMovieSort';
-export const SET_LIST_MOVIE_FILTER = 'addMovie/setListMovieFilter';
-export const SET_LIST_MOVIE_VIEW = 'addMovie/setListMovieView';
-export const SET_LIST_MOVIE_TABLE_OPTION = 'addMovie/setListMovieTableOption';
-export const SET_LIST_MOVIE_POSTER_OPTION = 'addMovie/setListMoviePosterOption';
-export const SET_LIST_MOVIE_OVERVIEW_OPTION = 'addMovie/setListMovieOverviewOption';
-
//
// Action Creators
@@ -272,16 +58,6 @@ export const addMovie = createThunk(ADD_MOVIE);
export const clearAddMovie = createAction(CLEAR_ADD_MOVIE);
export const setAddMovieDefault = createAction(SET_ADD_MOVIE_DEFAULT);
-export const fetchListMovies = createThunk(FETCH_LIST_MOVIES);
-export const fetchDiscoverMovies = createThunk(FETCH_DISCOVER_MOVIES);
-
-export const setListMovieSort = createAction(SET_LIST_MOVIE_SORT);
-export const setListMovieFilter = createAction(SET_LIST_MOVIE_FILTER);
-export const setListMovieView = createAction(SET_LIST_MOVIE_VIEW);
-export const setListMovieTableOption = createAction(SET_LIST_MOVIE_TABLE_OPTION);
-export const setListMoviePosterOption = createAction(SET_LIST_MOVIE_POSTER_OPTION);
-export const setListMovieOverviewOption = createAction(SET_LIST_MOVIE_OVERVIEW_OPTION);
-
export const setAddMovieValue = createAction(SET_ADD_MOVIE_VALUE, (payload) => {
return {
section,
@@ -294,10 +70,6 @@ export const setAddMovieValue = createAction(SET_ADD_MOVIE_VALUE, (payload) => {
export const actionHandlers = handleThunks({
- [FETCH_LIST_MOVIES]: createFetchHandler(section, '/netimport/movies'),
-
- [FETCH_DISCOVER_MOVIES]: createFetchHandler(section, '/movies/discover'),
-
[LOOKUP_MOVIE]: function(getState, payload, dispatch) {
dispatch(set({ section, isFetching: true }));
@@ -393,54 +165,14 @@ export const reducers = createHandleActions({
return updateSectionState(state, section, newState);
},
- [SET_LIST_MOVIE_SORT]: createSetClientSideCollectionSortReducer(section),
- [SET_LIST_MOVIE_FILTER]: createSetClientSideCollectionFilterReducer(section),
-
- [SET_LIST_MOVIE_VIEW]: function(state, { payload }) {
- return Object.assign({}, state, { view: payload.view });
- },
-
- [SET_LIST_MOVIE_TABLE_OPTION]: createSetTableOptionReducer(section),
-
- [SET_LIST_MOVIE_POSTER_OPTION]: function(state, { payload }) {
- const posterOptions = state.posterOptions;
+ [CLEAR_ADD_MOVIE]: function(state) {
+ const {
+ defaults,
+ view,
+ ...otherDefaultState
+ } = defaultState;
- return {
- ...state,
- posterOptions: {
- ...posterOptions,
- ...payload
- }
- };
- },
-
- [SET_LIST_MOVIE_OVERVIEW_OPTION]: function(state, { payload }) {
- const overviewOptions = state.overviewOptions;
-
- return {
- ...state,
- overviewOptions: {
- ...overviewOptions,
- ...payload
- }
- };
- },
-
- // [CLEAR_ADD_MOVIE]: function(state) {
- // const {
- // defaults,
- // view,
- // ...otherDefaultState
- // } = defaultState;
-
- // return Object.assign({}, state, otherDefaultState);
- // }
-
- [CLEAR_ADD_MOVIE]: createClearReducer(section, {
- isFetching: false,
- isPopulated: false,
- error: null,
- items: []
- })
+ return Object.assign({}, state, otherDefaultState);
+ }
}, defaultState, section);
diff --git a/frontend/src/Store/Actions/discoverMovieActions.js b/frontend/src/Store/Actions/discoverMovieActions.js
new file mode 100644
index 000000000..989bbd1ec
--- /dev/null
+++ b/frontend/src/Store/Actions/discoverMovieActions.js
@@ -0,0 +1,584 @@
+import _ from 'lodash';
+import { createAction } from 'redux-actions';
+import { batchActions } from 'redux-batched-actions';
+import getSectionState from 'Utilities/State/getSectionState';
+import updateSectionState from 'Utilities/State/updateSectionState';
+import createAjaxRequest from 'Utilities/createAjaxRequest';
+import getNewMovie from 'Utilities/Movie/getNewMovie';
+import { filterBuilderTypes, filterBuilderValueTypes, filterTypes, sortDirections } from 'Helpers/Props';
+import sortByName from 'Utilities/Array/sortByName';
+import { createThunk, handleThunks } from 'Store/thunks';
+import createSetSettingValueReducer from './Creators/Reducers/createSetSettingValueReducer';
+import createHandleActions from './Creators/createHandleActions';
+import { set, updateItem, removeItem } from './baseActions';
+import { filterPredicates } from './movieActions';
+import createSetTableOptionReducer from './Creators/Reducers/createSetTableOptionReducer';
+import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
+import createSetClientSideCollectionFilterReducer from './Creators/Reducers/createSetClientSideCollectionFilterReducer';
+import createClearReducer from './Creators/Reducers/createClearReducer';
+
+//
+// Variables
+
+export const section = 'discoverMovie';
+
+//
+// State
+
+export const defaultState = {
+ isFetching: false,
+ isPopulated: false,
+ error: null,
+ isAdding: false,
+ isAdded: false,
+ addError: null,
+ items: [],
+ sortKey: 'sortTitle',
+ sortDirection: sortDirections.ASCENDING,
+ secondarySortKey: 'sortTitle',
+ secondarySortDirection: sortDirections.ASCENDING,
+ view: 'overview',
+
+ defaults: {
+ rootFolderPath: '',
+ monitor: 'true',
+ qualityProfileId: 0,
+ minimumAvailability: 'announced',
+ tags: []
+ },
+
+ posterOptions: {
+ size: 'large',
+ showTitle: false
+ },
+
+ overviewOptions: {
+ detailedProgressBar: false,
+ size: 'medium',
+ showStudio: true
+ },
+
+ tableOptions: {
+ // showSearchAction: false
+ },
+
+ columns: [
+ {
+ name: 'status',
+ columnLabel: 'Status',
+ isSortable: true,
+ isVisible: true,
+ isModifiable: false
+ },
+ {
+ name: 'sortTitle',
+ label: 'Movie Title',
+ isSortable: true,
+ isVisible: true,
+ isModifiable: false
+ },
+ {
+ name: 'studio',
+ label: 'Studio',
+ isSortable: true,
+ isVisible: true
+ },
+ {
+ name: 'inCinemas',
+ label: 'In Cinemas',
+ isSortable: true,
+ isVisible: true
+ },
+ {
+ name: 'physicalRelease',
+ label: 'Physical Release',
+ isSortable: true,
+ isVisible: false
+ },
+ {
+ name: 'genres',
+ label: 'Genres',
+ isSortable: false,
+ isVisible: false
+ },
+ {
+ name: 'ratings',
+ label: 'Rating',
+ isSortable: true,
+ isVisible: false
+ },
+ {
+ name: 'certification',
+ label: 'Certification',
+ isSortable: true,
+ isVisible: false
+ },
+ {
+ name: 'actions',
+ columnLabel: 'Actions',
+ isVisible: true,
+ isModifiable: false
+ }
+ ],
+
+ sortPredicates: {
+ status: function(item) {
+ let result = 0;
+
+ if (item.isExcluded) {
+ result += 4;
+ }
+
+ if (item.status === 'announced') {
+ result++;
+ }
+
+ if (item.status === 'inCinemas') {
+ result += 2;
+ }
+
+ if (item.status === 'released') {
+ result += 3;
+ }
+
+ return result;
+ },
+
+ studio: function(item) {
+ const studio = item.studio;
+
+ return studio ? studio.toLowerCase() : '';
+ },
+
+ ratings: function(item) {
+ const { ratings = {} } = item;
+
+ return ratings.value;
+ }
+ },
+
+ selectedFilterKey: 'newNotExcluded',
+
+ filters: [
+ {
+ key: 'all',
+ label: 'All',
+ filters: []
+ },
+ {
+ key: 'newNotExcluded',
+ label: 'New Non-Excluded',
+ filters: [
+ {
+ key: 'isExisting',
+ value: false,
+ type: filterTypes.EQUAL
+ },
+ {
+ key: 'isExcluded',
+ value: false,
+ type: filterTypes.EQUAL
+ }
+ ]
+ }
+ ],
+
+ filterPredicates,
+
+ filterBuilderProps: [
+ {
+ name: 'status',
+ label: 'Status',
+ type: filterBuilderTypes.EXACT,
+ valueType: filterBuilderValueTypes.MOVIE_STATUS
+ },
+ {
+ name: 'studio',
+ label: 'Studio',
+ type: filterBuilderTypes.ARRAY,
+ optionsSelector: function(items) {
+ const tagList = items.reduce((acc, movie) => {
+ acc.push({
+ id: movie.studio,
+ name: movie.studio
+ });
+
+ return acc;
+ }, []);
+
+ return tagList.sort(sortByName);
+ }
+ },
+ {
+ name: 'inCinemas',
+ label: 'In Cinemas',
+ type: filterBuilderTypes.DATE,
+ valueType: filterBuilderValueTypes.DATE
+ },
+ {
+ name: 'physicalRelease',
+ label: 'Physical Release',
+ type: filterBuilderTypes.DATE,
+ valueType: filterBuilderValueTypes.DATE
+ },
+ {
+ name: 'genres',
+ label: 'Genres',
+ type: filterBuilderTypes.ARRAY,
+ optionsSelector: function(items) {
+ const tagList = items.reduce((acc, movie) => {
+ movie.genres.forEach((genre) => {
+ acc.push({
+ id: genre,
+ name: genre
+ });
+ });
+
+ return acc;
+ }, []);
+
+ return tagList.sort(sortByName);
+ }
+ },
+ {
+ name: 'ratings',
+ label: 'Rating',
+ type: filterBuilderTypes.NUMBER
+ },
+ {
+ name: 'certification',
+ label: 'Certification',
+ type: filterBuilderTypes.EXACT
+ },
+ {
+ name: 'isExcluded',
+ label: 'On Excluded List',
+ type: filterBuilderTypes.EXACT,
+ valueType: filterBuilderValueTypes.BOOL
+ },
+ {
+ name: 'isExisting',
+ label: 'Exists in Library',
+ type: filterBuilderTypes.EXACT,
+ valueType: filterBuilderValueTypes.BOOL
+ }
+ ]
+};
+
+export const persistState = [
+ 'discoverMovie.defaults',
+ 'discoverMovie.sortKey',
+ 'discoverMovie.sortDirection',
+ 'discoverMovie.selectedFilterKey',
+ 'discoverMovie.customFilters',
+ 'discoverMovie.view',
+ 'discoverMovie.columns',
+ 'discoverMovie.posterOptions',
+ 'discoverMovie.overviewOptions',
+ 'discoverMovie.tableOptions'
+];
+
+//
+// Actions Types
+
+export const ADD_MOVIE = 'discoverMovie/addMovie';
+export const ADD_MOVIES = 'discoverMovie/addMovies';
+export const SET_ADD_MOVIE_VALUE = 'discoverMovie/setAddMovieValue';
+export const CLEAR_ADD_MOVIE = 'discoverMovie/clearAddMovie';
+export const SET_ADD_MOVIE_DEFAULT = 'discoverMovie/setAddMovieDefault';
+
+export const FETCH_DISCOVER_MOVIES = 'discoverMovie/fetchDiscoverMovies';
+
+export const SET_LIST_MOVIE_SORT = 'discoverMovie/setListMovieSort';
+export const SET_LIST_MOVIE_FILTER = 'discoverMovie/setListMovieFilter';
+export const SET_LIST_MOVIE_VIEW = 'discoverMovie/setListMovieView';
+export const SET_LIST_MOVIE_TABLE_OPTION = 'discoverMovie/setListMovieTableOption';
+export const SET_LIST_MOVIE_POSTER_OPTION = 'discoverMovie/setListMoviePosterOption';
+export const SET_LIST_MOVIE_OVERVIEW_OPTION = 'discoverMovie/setListMovieOverviewOption';
+
+export const ADD_NET_IMPORT_EXCLUSIONS = 'discoverMovie/addNetImportExclusions';
+
+//
+// Action Creators
+
+export const addMovie = createThunk(ADD_MOVIE);
+export const addMovies = createThunk(ADD_MOVIES);
+export const clearAddMovie = createAction(CLEAR_ADD_MOVIE);
+export const setAddMovieDefault = createAction(SET_ADD_MOVIE_DEFAULT);
+
+export const fetchDiscoverMovies = createThunk(FETCH_DISCOVER_MOVIES);
+
+export const setListMovieSort = createAction(SET_LIST_MOVIE_SORT);
+export const setListMovieFilter = createAction(SET_LIST_MOVIE_FILTER);
+export const setListMovieView = createAction(SET_LIST_MOVIE_VIEW);
+export const setListMovieTableOption = createAction(SET_LIST_MOVIE_TABLE_OPTION);
+export const setListMoviePosterOption = createAction(SET_LIST_MOVIE_POSTER_OPTION);
+export const setListMovieOverviewOption = createAction(SET_LIST_MOVIE_OVERVIEW_OPTION);
+
+export const addNetImportExclusions = createThunk(ADD_NET_IMPORT_EXCLUSIONS);
+
+export const setAddMovieValue = createAction(SET_ADD_MOVIE_VALUE, (payload) => {
+ return {
+ section,
+ ...payload
+ };
+});
+
+//
+// Action Handlers
+
+export const actionHandlers = handleThunks({
+
+ [FETCH_DISCOVER_MOVIES]: function(getState, payload, dispatch) {
+ dispatch(set({ section, isFetching: true }));
+
+ const {
+ id,
+ ...otherPayload
+ } = payload;
+
+ const promise = createAjaxRequest({
+ url: '/movies/discover',
+ data: otherPayload,
+ traditional: true
+ }).request;
+
+ promise.done((data) => {
+ // set an Id so the selectors and updaters done blow up.
+ data = data.map((movie) => ({ ...movie, id: movie.tmdbId }));
+
+ dispatch(batchActions([
+ ...data.map((movie) => updateItem({ section, ...movie })),
+
+ set({
+ section,
+ isFetching: false,
+ isPopulated: true,
+ error: null
+ })
+ ]));
+ });
+
+ promise.fail((xhr) => {
+ dispatch(set({
+ section,
+ isFetching: false,
+ isPopulated: false,
+ error: xhr.aborted ? null : xhr
+ }));
+ });
+ },
+
+ [ADD_MOVIE]: function(getState, payload, dispatch) {
+ dispatch(set({ section, isAdding: true }));
+
+ const tmdbId = payload.tmdbId;
+ const items = getState().discoverMovie.items;
+ const itemToUpdate = _.find(items, { tmdbId });
+
+ const newMovie = getNewMovie(_.cloneDeep(itemToUpdate), payload);
+ newMovie.id = 0;
+
+ const promise = createAjaxRequest({
+ url: '/movie',
+ method: 'POST',
+ contentType: 'application/json',
+ data: JSON.stringify(newMovie)
+ }).request;
+
+ promise.done((data) => {
+ dispatch(batchActions([
+ updateItem({ section: 'movies', ...data }),
+
+ removeItem({ section: 'discoverMovie', ...itemToUpdate }),
+
+ set({
+ section,
+ isAdding: false,
+ isAdded: true,
+ addError: null
+ })
+ ]));
+ });
+
+ promise.fail((xhr) => {
+ dispatch(set({
+ section,
+ isAdding: false,
+ isAdded: false,
+ addError: xhr
+ }));
+ });
+ },
+
+ [ADD_MOVIES]: function(getState, payload, dispatch) {
+ dispatch(set({ section, isAdding: true }));
+
+ const ids = payload.ids;
+ const addOptions = payload.addOptions;
+ const items = getState().discoverMovie.items;
+ const addedIds = [];
+
+ const allNewMovies = ids.reduce((acc, id) => {
+ const item = items.find((i) => i.id === id);
+ const selectedMovie = item;
+
+ // Make sure we have a selected movie and
+ // the same movie hasn't been added yet.
+ if (selectedMovie && !acc.some((a) => a.tmdbId === selectedMovie.tmdbId)) {
+ const newMovie = getNewMovie(_.cloneDeep(selectedMovie), addOptions);
+ newMovie.id = 0;
+
+ addedIds.push(id);
+ acc.push(newMovie);
+ }
+
+ return acc;
+ }, []);
+
+ const promise = createAjaxRequest({
+ url: '/movie/import',
+ method: 'POST',
+ contentType: 'application/json',
+ data: JSON.stringify(allNewMovies)
+ }).request;
+
+ promise.done((data) => {
+ dispatch(batchActions([
+ set({
+ section,
+ isAdding: false,
+ isAdded: true
+ }),
+
+ ...data.map((movie) => updateItem({ section: 'movies', ...movie })),
+
+ ...addedIds.map((id) => removeItem({ section, id }))
+ ]));
+ });
+
+ promise.fail((xhr) => {
+ dispatch(batchActions(
+ set({
+ section,
+ isImporting: false,
+ isImported: true
+ }),
+
+ addedIds.map((id) => updateItem({
+ section,
+ id,
+ importError: xhr
+ }))
+ ));
+ });
+ },
+
+ [ADD_NET_IMPORT_EXCLUSIONS]: function(getState, payload, dispatch) {
+
+ const ids = payload.ids;
+ const items = getState().discoverMovie.items;
+
+ const exclusions = ids.reduce((acc, id) => {
+ const item = items.find((i) => i.tmdbId === id);
+
+ const newExclusion = {
+ tmdbId: id,
+ movieTitle: item.title,
+ movieYear: item.year
+ };
+
+ acc.push(newExclusion);
+
+ return acc;
+ }, []);
+
+ const promise = createAjaxRequest({
+ url: '/exclusions',
+ method: 'POST',
+ data: JSON.stringify(exclusions)
+ }).request;
+
+ promise.done((data) => {
+ dispatch(batchActions([
+ ...data.map((item) => updateItem({ section: 'settings.netImportExclusions', ...item })),
+
+ ...data.map((item) => updateItem({ section, id: item.tmdbId, isExcluded: true })),
+
+ set({
+ section,
+ isSaving: false,
+ saveError: null
+ })
+ ]));
+ });
+
+ promise.fail((xhr) => {
+ dispatch(set({
+ section,
+ isSaving: false,
+ saveError: xhr
+ }));
+ });
+ }
+});
+
+//
+// Reducers
+
+export const reducers = createHandleActions({
+
+ [SET_ADD_MOVIE_VALUE]: createSetSettingValueReducer(section),
+
+ [SET_ADD_MOVIE_DEFAULT]: function(state, { payload }) {
+ const newState = getSectionState(state, section);
+
+ newState.defaults = {
+ ...newState.defaults,
+ ...payload
+ };
+
+ return updateSectionState(state, section, newState);
+ },
+
+ [SET_LIST_MOVIE_SORT]: createSetClientSideCollectionSortReducer(section),
+ [SET_LIST_MOVIE_FILTER]: createSetClientSideCollectionFilterReducer(section),
+
+ [SET_LIST_MOVIE_VIEW]: function(state, { payload }) {
+ return Object.assign({}, state, { view: payload.view });
+ },
+
+ [SET_LIST_MOVIE_TABLE_OPTION]: createSetTableOptionReducer(section),
+
+ [SET_LIST_MOVIE_POSTER_OPTION]: function(state, { payload }) {
+ const posterOptions = state.posterOptions;
+
+ return {
+ ...state,
+ posterOptions: {
+ ...posterOptions,
+ ...payload
+ }
+ };
+ },
+
+ [SET_LIST_MOVIE_OVERVIEW_OPTION]: function(state, { payload }) {
+ const overviewOptions = state.overviewOptions;
+
+ return {
+ ...state,
+ overviewOptions: {
+ ...overviewOptions,
+ ...payload
+ }
+ };
+ },
+
+ [CLEAR_ADD_MOVIE]: createClearReducer(section, {
+ isFetching: false,
+ isPopulated: false,
+ error: null,
+ items: []
+ })
+
+}, defaultState, section);
diff --git a/frontend/src/Store/Actions/index.js b/frontend/src/Store/Actions/index.js
index 03d201680..281739940 100644
--- a/frontend/src/Store/Actions/index.js
+++ b/frontend/src/Store/Actions/index.js
@@ -5,6 +5,7 @@ import * as calendar from './calendarActions';
import * as captcha from './captchaActions';
import * as customFilters from './customFilterActions';
import * as commands from './commandActions';
+import * as discoverMovie from './discoverMovieActions';
import * as movieFiles from './movieFileActions';
import * as extraFiles from './extraFileActions';
import * as history from './historyActions';
@@ -33,6 +34,7 @@ export default [
captcha,
commands,
customFilters,
+ discoverMovie,
movieFiles,
extraFiles,
history,
diff --git a/frontend/src/Store/Selectors/createAddMovieClientSideCollectionItemsSelector.js b/frontend/src/Store/Selectors/createDiscoverMovieClientSideCollectionItemsSelector.js
similarity index 83%
rename from frontend/src/Store/Selectors/createAddMovieClientSideCollectionItemsSelector.js
rename to frontend/src/Store/Selectors/createDiscoverMovieClientSideCollectionItemsSelector.js
index 24ec81bbe..04fcd4f03 100644
--- a/frontend/src/Store/Selectors/createAddMovieClientSideCollectionItemsSelector.js
+++ b/frontend/src/Store/Selectors/createDiscoverMovieClientSideCollectionItemsSelector.js
@@ -26,11 +26,11 @@ function createUnoptimizedSelector(uiSection) {
);
}
-function createAddMovieClientSideCollectionItemsSelector(uiSection) {
+function createDiscoverMovieClientSideCollectionItemsSelector(uiSection) {
return createDeepEqualSelector(
createUnoptimizedSelector(uiSection),
(movies) => movies
);
}
-export default createAddMovieClientSideCollectionItemsSelector;
+export default createDiscoverMovieClientSideCollectionItemsSelector;
diff --git a/frontend/src/Store/Selectors/createAddListMovieSelector.js b/frontend/src/Store/Selectors/createDiscoverMovieSelector.js
similarity index 64%
rename from frontend/src/Store/Selectors/createAddListMovieSelector.js
rename to frontend/src/Store/Selectors/createDiscoverMovieSelector.js
index 99fd1b39e..da5bc282a 100644
--- a/frontend/src/Store/Selectors/createAddListMovieSelector.js
+++ b/frontend/src/Store/Selectors/createDiscoverMovieSelector.js
@@ -1,13 +1,13 @@
import { createSelector } from 'reselect';
-function createAddListMovieSelector() {
+function createDiscoverMovieSelector() {
return createSelector(
(state, { movieId }) => movieId,
- (state) => state.addMovie,
+ (state) => state.discoverMovie,
(movieId, allMovies) => {
return allMovies.items.find((movie) => movie.tmdbId === movieId);
}
);
}
-export default createAddListMovieSelector;
+export default createDiscoverMovieSelector;
diff --git a/frontend/src/Store/scrollPositions.js b/frontend/src/Store/scrollPositions.js
index 5e822c568..99a558b4d 100644
--- a/frontend/src/Store/scrollPositions.js
+++ b/frontend/src/Store/scrollPositions.js
@@ -1,6 +1,6 @@
const scrollPositions = {
movieIndex: 0,
- addMovie: 0
+ discoverMovie: 0
};
export default scrollPositions;
diff --git a/frontend/src/Styles/Variables/dimensions.js b/frontend/src/Styles/Variables/dimensions.js
index c59846531..a277f51dc 100644
--- a/frontend/src/Styles/Variables/dimensions.js
+++ b/frontend/src/Styles/Variables/dimensions.js
@@ -47,7 +47,7 @@ module.exports = {
modalBodyPadding: '30px',
// Movie
- movieIndexColumnPadding: '20px',
- movieIndexColumnPaddingSmallScreen: '10px',
+ movieIndexColumnPadding: '10px',
+ movieIndexColumnPaddingSmallScreen: '5px',
movieIndexOverviewInfoRowHeight: '21px'
};
diff --git a/frontend/src/Utilities/Table/toggleSelected.js b/frontend/src/Utilities/Table/toggleSelected.js
index dbc0d6223..d03dcef36 100644
--- a/frontend/src/Utilities/Table/toggleSelected.js
+++ b/frontend/src/Utilities/Table/toggleSelected.js
@@ -1,7 +1,7 @@
import areAllSelected from './areAllSelected';
import getToggledRange from './getToggledRange';
-function toggleSelected(state, items, id, selected, shiftKey) {
+function toggleSelected(state, items, id, selected, shiftKey, idProp = 'id') {
const lastToggled = state.lastToggled;
const selectedState = {
...state.selectedState,
@@ -16,7 +16,7 @@ function toggleSelected(state, items, id, selected, shiftKey) {
const { lower, upper } = getToggledRange(items, id, lastToggled);
for (let i = lower; i < upper; i++) {
- selectedState[items[i].id] = selected;
+ selectedState[items[i][idProp]] = selected;
}
}
diff --git a/src/NzbDrone.Core/NetImport/ImportExclusions/ImportExclusionsService.cs b/src/NzbDrone.Core/NetImport/ImportExclusions/ImportExclusionsService.cs
index 072d1a4b8..a938f1e72 100644
--- a/src/NzbDrone.Core/NetImport/ImportExclusions/ImportExclusionsService.cs
+++ b/src/NzbDrone.Core/NetImport/ImportExclusions/ImportExclusionsService.cs
@@ -11,6 +11,7 @@ namespace NzbDrone.Core.NetImport.ImportExclusions
List GetAllExclusions();
bool IsMovieExcluded(int tmdbId);
ImportExclusion AddExclusion(ImportExclusion exclusion);
+ List AddExclusions(List exclusions);
void RemoveExclusion(ImportExclusion exclusion);
ImportExclusion GetById(int id);
}
@@ -37,6 +38,13 @@ namespace NzbDrone.Core.NetImport.ImportExclusions
return _exclusionRepository.Insert(exclusion);
}
+ public List AddExclusions(List exclusions)
+ {
+ _exclusionRepository.InsertMany(exclusions);
+
+ return exclusions;
+ }
+
public List GetAllExclusions()
{
return _exclusionRepository.All().ToList();
diff --git a/src/Radarr.Api.V3/NetImport/ImportExclusionsModule.cs b/src/Radarr.Api.V3/NetImport/ImportExclusionsModule.cs
index ad440f1ed..44d650b09 100644
--- a/src/Radarr.Api.V3/NetImport/ImportExclusionsModule.cs
+++ b/src/Radarr.Api.V3/NetImport/ImportExclusionsModule.cs
@@ -3,6 +3,7 @@ using FluentValidation;
using NzbDrone.Core.NetImport;
using NzbDrone.Core.NetImport.ImportExclusions;
using Radarr.Http;
+using Radarr.Http.Extensions;
namespace Radarr.Api.V3.NetImport
{
@@ -15,9 +16,9 @@ namespace Radarr.Api.V3.NetImport
{
_exclusionService = exclusionService;
GetResourceAll = GetAll;
- CreateResource = AddExclusion;
DeleteResource = RemoveExclusion;
GetResourceById = GetById;
+ Post("/", x => AddExclusions());
SharedValidator.RuleFor(c => c.TmdbId).GreaterThan(0);
SharedValidator.RuleFor(c => c.MovieTitle).NotEmpty();
@@ -34,12 +35,13 @@ namespace Radarr.Api.V3.NetImport
return _exclusionService.GetById(id).ToResource();
}
- public int AddExclusion(ImportExclusionsResource exclusionResource)
+ public object AddExclusions()
{
- var model = exclusionResource.ToModel();
+ var resource = Request.Body.FromJson>();
+ var newMovies = resource.ToModel();
// TODO: Add some more validation here and auto pull the title if not provided
- return _exclusionService.AddExclusion(model).Id;
+ return _exclusionService.AddExclusions(newMovies).ToResource();
}
public void RemoveExclusion(int id)
diff --git a/src/Radarr.Api.V3/NetImport/ImportExclusionsResource.cs b/src/Radarr.Api.V3/NetImport/ImportExclusionsResource.cs
index 718819e0a..123ed1098 100644
--- a/src/Radarr.Api.V3/NetImport/ImportExclusionsResource.cs
+++ b/src/Radarr.Api.V3/NetImport/ImportExclusionsResource.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
+using NzbDrone.Core.NetImport.ImportExclusions;
namespace Radarr.Api.V3.NetImport
{
@@ -13,7 +14,7 @@ namespace Radarr.Api.V3.NetImport
public static class ImportExclusionsResourceMapper
{
- public static ImportExclusionsResource ToResource(this NzbDrone.Core.NetImport.ImportExclusions.ImportExclusion model)
+ public static ImportExclusionsResource ToResource(this ImportExclusion model)
{
if (model == null)
{
@@ -29,19 +30,24 @@ namespace Radarr.Api.V3.NetImport
};
}
- public static List ToResource(this IEnumerable exclusions)
+ public static List ToResource(this IEnumerable exclusions)
{
return exclusions.Select(ToResource).ToList();
}
- public static NzbDrone.Core.NetImport.ImportExclusions.ImportExclusion ToModel(this ImportExclusionsResource resource)
+ public static ImportExclusion ToModel(this ImportExclusionsResource resource)
{
- return new NzbDrone.Core.NetImport.ImportExclusions.ImportExclusion
+ return new ImportExclusion
{
TmdbId = resource.TmdbId,
MovieTitle = resource.MovieTitle,
MovieYear = resource.MovieYear
};
}
+
+ public static List ToModel(this IEnumerable resources)
+ {
+ return resources.Select(ToModel).ToList();
+ }
}
}