1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-07-07 04:19:25 +02:00

New: Include trending and popular options for Discover Movies

This commit is contained in:
Bogdan 2024-05-11 16:29:07 +03:00
parent 7be8a34130
commit 685f462959
8 changed files with 198 additions and 44 deletions

View File

@ -75,9 +75,19 @@ class DiscoverMovie extends Component {
const { const {
items, items,
sortKey, sortKey,
sortDirection sortDirection,
includeRecommendations,
includeTrending,
includePopular
} = this.props; } = this.props;
if (includeRecommendations !== prevProps.includeRecommendations ||
includeTrending !== prevProps.includeTrending ||
includePopular !== prevProps.includePopular
) {
this.props.dispatchFetchListMovies();
}
if (sortKey !== prevProps.sortKey || if (sortKey !== prevProps.sortKey ||
sortDirection !== prevProps.sortDirection || sortDirection !== prevProps.sortDirection ||
hasDifferentItemsOrOrder(prevProps.items, items) hasDifferentItemsOrOrder(prevProps.items, items)
@ -443,6 +453,9 @@ DiscoverMovie.propTypes = {
sortKey: PropTypes.string, sortKey: PropTypes.string,
sortDirection: PropTypes.oneOf(sortDirections.all), sortDirection: PropTypes.oneOf(sortDirections.all),
view: PropTypes.string.isRequired, view: PropTypes.string.isRequired,
includeRecommendations: PropTypes.bool.isRequired,
includeTrending: PropTypes.bool.isRequired,
includePopular: PropTypes.bool.isRequired,
isSyncingLists: PropTypes.bool.isRequired, isSyncingLists: PropTypes.bool.isRequired,
isSmallScreen: PropTypes.bool.isRequired, isSmallScreen: PropTypes.bool.isRequired,
onSortSelect: PropTypes.func.isRequired, onSortSelect: PropTypes.func.isRequired,
@ -451,7 +464,8 @@ DiscoverMovie.propTypes = {
onScroll: PropTypes.func.isRequired, onScroll: PropTypes.func.isRequired,
onAddMoviesPress: PropTypes.func.isRequired, onAddMoviesPress: PropTypes.func.isRequired,
onExcludeMoviesPress: PropTypes.func.isRequired, onExcludeMoviesPress: PropTypes.func.isRequired,
onImportListSyncPress: PropTypes.func.isRequired onImportListSyncPress: PropTypes.func.isRequired,
dispatchFetchListMovies: PropTypes.func.isRequired
}; };
export default DiscoverMovie; export default DiscoverMovie;

View File

@ -17,15 +17,18 @@ import DiscoverMovie from './DiscoverMovie';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
(state) => state.discoverMovie,
createDiscoverMovieClientSideCollectionItemsSelector('discoverMovie'), createDiscoverMovieClientSideCollectionItemsSelector('discoverMovie'),
createCommandExecutingSelector(commandNames.IMPORT_LIST_SYNC), createCommandExecutingSelector(commandNames.IMPORT_LIST_SYNC),
createDimensionsSelector(), createDimensionsSelector(),
( (
discoverMovie,
movies, movies,
isSyncingLists, isSyncingLists,
dimensionsState dimensionsState
) => { ) => {
return { return {
...discoverMovie.options,
...movies, ...movies,
isSyncingLists, isSyncingLists,
isSmallScreen: dimensionsState.isSmallScreen isSmallScreen: dimensionsState.isSmallScreen

View File

@ -49,7 +49,9 @@ class DiscoverMovieOverviewOptionsModalContent extends Component {
showRatings: props.showRatings, showRatings: props.showRatings,
showYear: props.showYear, showYear: props.showYear,
showGenres: props.showGenres, showGenres: props.showGenres,
includeRecommendations: props.includeRecommendations includeRecommendations: props.includeRecommendations,
includeTrending: props.includeTrending,
includePopular: props.includePopular
}; };
} }
@ -61,7 +63,9 @@ class DiscoverMovieOverviewOptionsModalContent extends Component {
showRatings, showRatings,
showCertification, showCertification,
showGenres, showGenres,
includeRecommendations includeRecommendations,
includeTrending,
includePopular
} = this.props; } = this.props;
const state = {}; const state = {};
@ -94,6 +98,14 @@ class DiscoverMovieOverviewOptionsModalContent extends Component {
state.includeRecommendations = includeRecommendations; state.includeRecommendations = includeRecommendations;
} }
if (includeTrending !== prevProps.includeTrending) {
state.includeTrending = includeTrending;
}
if (includePopular !== prevProps.includePopular) {
state.includePopular = includePopular;
}
if (!_.isEmpty(state)) { if (!_.isEmpty(state)) {
this.setState(state); this.setState(state);
} }
@ -135,19 +147,22 @@ class DiscoverMovieOverviewOptionsModalContent extends Component {
showRatings, showRatings,
showYear, showYear,
showGenres, showGenres,
includeRecommendations includeRecommendations,
includeTrending,
includePopular
} = this.state; } = this.state;
return ( return (
<ModalContent onModalClose={onModalClose}> <ModalContent onModalClose={onModalClose}>
<ModalHeader> <ModalHeader>
Overview Options {translate('OverviewOptions')}
</ModalHeader> </ModalHeader>
<ModalBody> <ModalBody>
<Form> <Form>
<FormGroup> <FormGroup>
<FormLabel>{translate('IncludeRadarrRecommendations')}</FormLabel> <FormLabel>{translate('IncludeRadarrRecommendations')}</FormLabel>
<FormInputGroup <FormInputGroup
type={inputTypes.CHECK} type={inputTypes.CHECK}
name="includeRecommendations" name="includeRecommendations"
@ -157,6 +172,30 @@ class DiscoverMovieOverviewOptionsModalContent extends Component {
/> />
</FormGroup> </FormGroup>
<FormGroup>
<FormLabel>{translate('IncludeTrending')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="includeTrending"
value={includeTrending}
helpText={translate('IncludeTrendingMoviesHelpText')}
onChange={this.onChangeOption}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('IncludePopular')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="includePopular"
value={includePopular}
helpText={translate('IncludePopularMoviesHelpText')}
onChange={this.onChangeOption}
/>
</FormGroup>
<FormGroup> <FormGroup>
<FormLabel>{translate('PosterSize')}</FormLabel> <FormLabel>{translate('PosterSize')}</FormLabel>
@ -246,6 +285,8 @@ DiscoverMovieOverviewOptionsModalContent.propTypes = {
showCertification: PropTypes.bool.isRequired, showCertification: PropTypes.bool.isRequired,
showGenres: PropTypes.bool.isRequired, showGenres: PropTypes.bool.isRequired,
includeRecommendations: PropTypes.bool.isRequired, includeRecommendations: PropTypes.bool.isRequired,
includeTrending: PropTypes.bool.isRequired,
includePopular: PropTypes.bool.isRequired,
onChangeOverviewOption: PropTypes.func.isRequired, onChangeOverviewOption: PropTypes.func.isRequired,
onChangeOption: PropTypes.func.isRequired, onChangeOption: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired onModalClose: PropTypes.func.isRequired

View File

@ -45,7 +45,9 @@ class DiscoverMoviePosterOptionsModalContent extends Component {
this.state = { this.state = {
size: props.size, size: props.size,
showTitle: props.showTitle, showTitle: props.showTitle,
includeRecommendations: props.includeRecommendations includeRecommendations: props.includeRecommendations,
includeTrending: props.includeTrending,
includePopular: props.includePopular
}; };
} }
@ -53,7 +55,9 @@ class DiscoverMoviePosterOptionsModalContent extends Component {
const { const {
size, size,
showTitle, showTitle,
includeRecommendations includeRecommendations,
includeTrending,
includePopular
} = this.props; } = this.props;
const state = {}; const state = {};
@ -70,6 +74,14 @@ class DiscoverMoviePosterOptionsModalContent extends Component {
state.includeRecommendations = includeRecommendations; state.includeRecommendations = includeRecommendations;
} }
if (includeTrending !== prevProps.includeTrending) {
state.includeTrending = includeTrending;
}
if (includePopular !== prevProps.includePopular) {
state.includePopular = includePopular;
}
if (!_.isEmpty(state)) { if (!_.isEmpty(state)) {
this.setState(state); this.setState(state);
} }
@ -107,13 +119,15 @@ class DiscoverMoviePosterOptionsModalContent extends Component {
const { const {
size, size,
showTitle, showTitle,
includeRecommendations includeRecommendations,
includeTrending,
includePopular
} = this.state; } = this.state;
return ( return (
<ModalContent onModalClose={onModalClose}> <ModalContent onModalClose={onModalClose}>
<ModalHeader> <ModalHeader>
Poster Options {translate('PosterOptions')}
</ModalHeader> </ModalHeader>
<ModalBody> <ModalBody>
@ -130,6 +144,30 @@ class DiscoverMoviePosterOptionsModalContent extends Component {
/> />
</FormGroup> </FormGroup>
<FormGroup>
<FormLabel>{translate('IncludeTrending')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="includeTrending"
value={includeTrending}
helpText={translate('IncludeTrendingMoviesHelpText')}
onChange={this.onChangeOption}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('IncludePopular')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="includePopular"
value={includePopular}
helpText={translate('IncludePopularMoviesHelpText')}
onChange={this.onChangeOption}
/>
</FormGroup>
<FormGroup> <FormGroup>
<FormLabel>{translate('PosterSize')}</FormLabel> <FormLabel>{translate('PosterSize')}</FormLabel>
@ -172,6 +210,8 @@ DiscoverMoviePosterOptionsModalContent.propTypes = {
size: PropTypes.string.isRequired, size: PropTypes.string.isRequired,
showTitle: PropTypes.bool.isRequired, showTitle: PropTypes.bool.isRequired,
includeRecommendations: PropTypes.bool.isRequired, includeRecommendations: PropTypes.bool.isRequired,
includeTrending: PropTypes.bool.isRequired,
includePopular: PropTypes.bool.isRequired,
onChangePosterOption: PropTypes.func.isRequired, onChangePosterOption: PropTypes.func.isRequired,
onChangeOption: PropTypes.func.isRequired, onChangeOption: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired onModalClose: PropTypes.func.isRequired

View File

@ -15,17 +15,29 @@ class DiscoverMovieTableOptions extends Component {
super(props, context); super(props, context);
this.state = { this.state = {
includeRecommendations: props.includeRecommendations includeRecommendations: props.includeRecommendations,
includeTrending: props.includeTrending,
includePopular: props.includePopular
}; };
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
const { includeRecommendations } = this.props; const {
includeRecommendations,
includeTrending,
includePopular
} = this.props;
if (includeRecommendations !== prevProps.includeRecommendations) { if (includeRecommendations !== prevProps.includeRecommendations) {
this.setState({ this.setState({ includeRecommendations });
includeRecommendations }
});
if (includeTrending !== prevProps.includeTrending) {
this.setState({ includeTrending });
}
if (includePopular !== prevProps.includePopular) {
this.setState({ includePopular });
} }
} }
@ -47,10 +59,13 @@ class DiscoverMovieTableOptions extends Component {
render() { render() {
const { const {
includeRecommendations includeRecommendations,
includeTrending,
includePopular
} = this.state; } = this.state;
return ( return (
<>
<FormGroup> <FormGroup>
<FormLabel>{translate('IncludeRadarrRecommendations')}</FormLabel> <FormLabel>{translate('IncludeRadarrRecommendations')}</FormLabel>
@ -62,12 +77,39 @@ class DiscoverMovieTableOptions extends Component {
onChange={this.onChangeOption} onChange={this.onChangeOption}
/> />
</FormGroup> </FormGroup>
<FormGroup>
<FormLabel>{translate('IncludeTrending')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="includeTrending"
value={includeTrending}
helpText={translate('IncludeTrendingMoviesHelpText')}
onChange={this.onChangeOption}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('IncludePopular')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="includePopular"
value={includePopular}
helpText={translate('IncludePopularMoviesHelpText')}
onChange={this.onChangeOption}
/>
</FormGroup>
</>
); );
} }
} }
DiscoverMovieTableOptions.propTypes = { DiscoverMovieTableOptions.propTypes = {
includeRecommendations: PropTypes.bool.isRequired, includeRecommendations: PropTypes.bool.isRequired,
includeTrending: PropTypes.bool.isRequired,
includePopular: PropTypes.bool.isRequired,
onChangeOption: PropTypes.func.isRequired onChangeOption: PropTypes.func.isRequired
}; };

View File

@ -42,7 +42,9 @@ export const defaultState = {
view: 'overview', view: 'overview',
options: { options: {
includeRecommendations: true includeRecommendations: true,
includeTrending: true,
includePopular: true
}, },
defaults: { defaults: {
@ -583,10 +585,14 @@ export const actionHandlers = handleThunks({
...otherPayload ...otherPayload
} = payload; } = payload;
const includeRecommendations = getState().discoverMovie.options.includeRecommendations; const {
includeRecommendations = false,
includeTrending = false,
includePopular = false
} = getState().discoverMovie.options;
const promise = createAjaxRequest({ const promise = createAjaxRequest({
url: `/importlist/movie?includeRecommendations=${includeRecommendations}`, url: `/importlist/movie?includeRecommendations=${includeRecommendations}&includeTrending=${includeTrending}&includePopular=${includePopular}`,
data: otherPayload, data: otherPayload,
traditional: true traditional: true
}).request; }).request;

View File

@ -731,8 +731,12 @@
"IncludeCustomFormatWhenRenaming": "Include Custom Format when Renaming", "IncludeCustomFormatWhenRenaming": "Include Custom Format when Renaming",
"IncludeCustomFormatWhenRenamingHelpText": "Include in {Custom Formats} renaming format", "IncludeCustomFormatWhenRenamingHelpText": "Include in {Custom Formats} renaming format",
"IncludeHealthWarnings": "Include Health Warnings", "IncludeHealthWarnings": "Include Health Warnings",
"IncludePopular": "Include Popular",
"IncludePopularMoviesHelpText": "Include popular movies on TMDb",
"IncludeRadarrRecommendations": "Include {appName} Recommendations", "IncludeRadarrRecommendations": "Include {appName} Recommendations",
"IncludeRecommendationsHelpText": "Include {appName} recommended movies in discovery view", "IncludeRecommendationsHelpText": "Include {appName} recommended movies in discovery view",
"IncludeTrending": "Include Trending",
"IncludeTrendingMoviesHelpText": "Include trending movies on TMDb",
"IncludeUnmonitored": "Include Unmonitored", "IncludeUnmonitored": "Include Unmonitored",
"Indexer": "Indexer", "Indexer": "Indexer",
"IndexerDownloadClientHealthCheckMessage": "Indexers with invalid download clients: {0}.", "IndexerDownloadClientHealthCheckMessage": "Indexers with invalid download clients: {0}.",

View File

@ -64,28 +64,31 @@ public object GetDiscoverMovies(bool includeRecommendations = false, bool includ
if (includeRecommendations) if (includeRecommendations)
{ {
var mapped = new List<Movie>(); var recommendedResults = _movieService.GetRecommendedTmdbIds();
var results = _movieService.GetRecommendedTmdbIds(); if (recommendedResults.Count > 0)
if (results.Count > 0)
{ {
mapped = _movieInfo.GetBulkMovieInfo(results).Select(m => new Movie { MovieMetadata = m }).ToList(); var mapped = _movieInfo.GetBulkMovieInfo(recommendedResults).Select(m => new Movie { MovieMetadata = m }).ToList();
}
realResults.AddRange(MapToResource(mapped.Where(x => x != null), movieLanguage, isRecommendation: true));
realResults.AddRange(MapToResource(mapped.Where(x => x != null), movieLanguage)); }
realResults.ForEach(x => x.IsRecommendation = true);
} }
if (includeTrending)
{
// Add TMDB Trending // Add TMDB Trending
var trendingResults = _movieInfo.GetTrendingMovies(); var trendingResults = _movieInfo.GetTrendingMovies();
realResults.AddRange(MapToResource(trendingResults.Select(m => new Movie { MovieMetadata = m }).Where(x => x != null), movieLanguage, true)); realResults.AddRange(MapToResource(trendingResults.Select(m => new Movie { MovieMetadata = m }).Where(x => x != null), movieLanguage, isTrending: true));
}
if (includePopular)
{
// Add TMDB Popular // Add TMDB Popular
var popularResults = _movieInfo.GetPopularMovies(); var popularResults = _movieInfo.GetPopularMovies();
realResults.AddRange(MapToResource(popularResults.Select(m => new Movie { MovieMetadata = m }).Where(x => x != null), movieLanguage, false, true)); realResults.AddRange(MapToResource(popularResults.Select(m => new Movie { MovieMetadata = m }).Where(x => x != null), movieLanguage, isPopular: true));
}
// Add List Movies // Add List Movies
var listMovies = MapToResource(_listMovieService.GetAllForLists(_importListFactory.Enabled().Select(x => x.Definition.Id).ToList()), movieLanguage).ToList(); var listMovies = MapToResource(_listMovieService.GetAllForLists(_importListFactory.Enabled().Select(x => x.Definition.Id).ToList()), movieLanguage).ToList();
@ -120,7 +123,7 @@ public object AddMovies([FromBody] List<MovieResource> resource)
return _addMovieService.AddMovies(newMovies, true).ToResource(0); return _addMovieService.AddMovies(newMovies, true).ToResource(0);
} }
private IEnumerable<ImportListMoviesResource> MapToResource(IEnumerable<Movie> movies, Language language, bool isTrending = false, bool isPopular = false) private IEnumerable<ImportListMoviesResource> MapToResource(IEnumerable<Movie> movies, Language language, bool isRecommendation = false, bool isTrending = false, bool isPopular = false)
{ {
// Avoid calling for naming spec on every movie in filenamebuilder // Avoid calling for naming spec on every movie in filenamebuilder
var namingConfig = _namingService.GetConfig(); var namingConfig = _namingService.GetConfig();
@ -140,6 +143,7 @@ private IEnumerable<ImportListMoviesResource> MapToResource(IEnumerable<Movie> m
resource.Title = translation?.Title ?? resource.Title; resource.Title = translation?.Title ?? resource.Title;
resource.Overview = translation?.Overview ?? resource.Overview; resource.Overview = translation?.Overview ?? resource.Overview;
resource.Folder = _fileNameBuilder.GetMovieFolder(currentMovie, namingConfig); resource.Folder = _fileNameBuilder.GetMovieFolder(currentMovie, namingConfig);
resource.IsRecommendation = isRecommendation;
resource.IsTrending = isTrending; resource.IsTrending = isTrending;
resource.IsPopular = isPopular; resource.IsPopular = isPopular;