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

View File

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

View File

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

View File

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

View File

@ -15,17 +15,29 @@ class DiscoverMovieTableOptions extends Component {
super(props, context);
this.state = {
includeRecommendations: props.includeRecommendations
includeRecommendations: props.includeRecommendations,
includeTrending: props.includeTrending,
includePopular: props.includePopular
};
}
componentDidUpdate(prevProps) {
const { includeRecommendations } = this.props;
const {
includeRecommendations,
includeTrending,
includePopular
} = this.props;
if (includeRecommendations !== prevProps.includeRecommendations) {
this.setState({
includeRecommendations
});
this.setState({ includeRecommendations });
}
if (includeTrending !== prevProps.includeTrending) {
this.setState({ includeTrending });
}
if (includePopular !== prevProps.includePopular) {
this.setState({ includePopular });
}
}
@ -47,27 +59,57 @@ class DiscoverMovieTableOptions extends Component {
render() {
const {
includeRecommendations
includeRecommendations,
includeTrending,
includePopular
} = this.state;
return (
<FormGroup>
<FormLabel>{translate('IncludeRadarrRecommendations')}</FormLabel>
<>
<FormGroup>
<FormLabel>{translate('IncludeRadarrRecommendations')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="includeRecommendations"
value={includeRecommendations}
helpText={translate('IncludeRecommendationsHelpText')}
onChange={this.onChangeOption}
/>
</FormGroup>
<FormInputGroup
type={inputTypes.CHECK}
name="includeRecommendations"
value={includeRecommendations}
helpText={translate('IncludeRecommendationsHelpText')}
onChange={this.onChangeOption}
/>
</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 = {
includeRecommendations: PropTypes.bool.isRequired,
includeTrending: PropTypes.bool.isRequired,
includePopular: PropTypes.bool.isRequired,
onChangeOption: PropTypes.func.isRequired
};

View File

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

View File

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

View File

@ -64,28 +64,31 @@ namespace Radarr.Api.V3.ImportLists
if (includeRecommendations)
{
var mapped = new List<Movie>();
var recommendedResults = _movieService.GetRecommendedTmdbIds();
var results = _movieService.GetRecommendedTmdbIds();
if (results.Count > 0)
if (recommendedResults.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));
realResults.ForEach(x => x.IsRecommendation = true);
realResults.AddRange(MapToResource(mapped.Where(x => x != null), movieLanguage, isRecommendation: true));
}
}
// Add TMDB Trending
var trendingResults = _movieInfo.GetTrendingMovies();
if (includeTrending)
{
// Add TMDB Trending
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));
}
// Add TMDB Popular
var popularResults = _movieInfo.GetPopularMovies();
if (includePopular)
{
// Add TMDB Popular
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
var listMovies = MapToResource(_listMovieService.GetAllForLists(_importListFactory.Enabled().Select(x => x.Definition.Id).ToList()), movieLanguage).ToList();
@ -120,7 +123,7 @@ namespace Radarr.Api.V3.ImportLists
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
var namingConfig = _namingService.GetConfig();
@ -140,6 +143,7 @@ namespace Radarr.Api.V3.ImportLists
resource.Title = translation?.Title ?? resource.Title;
resource.Overview = translation?.Overview ?? resource.Overview;
resource.Folder = _fileNameBuilder.GetMovieFolder(currentMovie, namingConfig);
resource.IsRecommendation = isRecommendation;
resource.IsTrending = isTrending;
resource.IsPopular = isPopular;