1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-11-19 17:32:38 +01:00

New: Trending and Popular Movies in Discovery

This commit is contained in:
Qstick 2024-01-27 20:24:56 -06:00
parent 3b1d4460ad
commit 0be449033f
15 changed files with 221 additions and 5 deletions

View File

@ -19,7 +19,7 @@ function ImportListList({ lists, importListList }) {
return ( return (
<Label <Label
key={list.id} key={list.id}
kind={kinds.INFO} kind={kinds.SUCCESS}
size={sizes.MEDIUM} size={sizes.MEDIUM}
> >
{list.name} {list.name}

View File

@ -97,6 +97,8 @@ class DiscoverMovieOverview extends Component {
isExisting, isExisting,
isExcluded, isExcluded,
isRecommendation, isRecommendation,
isPopular,
isTrending,
isSelected, isSelected,
overviewOptions, overviewOptions,
...otherProps ...otherProps
@ -214,6 +216,26 @@ class DiscoverMovieOverview extends Component {
null null
} }
{
isPopular ?
<Label
kind={kinds.INFO}
>
{translate('Popular')}
</Label> :
null
}
{
isTrending ?
<Label
kind={kinds.INFO}
>
{translate('Trending')}
</Label> :
null
}
<ImportListListConnector <ImportListListConnector
lists={lists} lists={lists}
/> />
@ -283,6 +305,8 @@ DiscoverMovieOverview.propTypes = {
isExisting: PropTypes.bool.isRequired, isExisting: PropTypes.bool.isRequired,
isExcluded: PropTypes.bool.isRequired, isExcluded: PropTypes.bool.isRequired,
isRecommendation: PropTypes.bool.isRequired, isRecommendation: PropTypes.bool.isRequired,
isPopular: PropTypes.bool.isRequired,
isTrending: PropTypes.bool.isRequired,
isSelected: PropTypes.bool, isSelected: PropTypes.bool,
lists: PropTypes.arrayOf(PropTypes.number).isRequired, lists: PropTypes.arrayOf(PropTypes.number).isRequired,
onSelectedChange: PropTypes.func.isRequired onSelectedChange: PropTypes.func.isRequired

View File

@ -57,10 +57,12 @@
flex: 0 0 115px; flex: 0 0 115px;
} }
.isTrending,
.isPopular,
.isRecommendation { .isRecommendation {
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css'; composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
flex: 0 0 50px; flex: 0 0 30px;
} }
.actions { .actions {

View File

@ -7,7 +7,9 @@ interface CssExports {
'digitalRelease': string; 'digitalRelease': string;
'genres': string; 'genres': string;
'inCinemas': string; 'inCinemas': string;
'isPopular': string;
'isRecommendation': string; 'isRecommendation': string;
'isTrending': string;
'lists': string; 'lists': string;
'originalLanguage': string; 'originalLanguage': string;
'physicalRelease': string; 'physicalRelease': string;

View File

@ -103,6 +103,40 @@ class DiscoverMovieHeader extends Component {
); );
} }
if (name === 'isTrending') {
return (
<VirtualTableHeaderCell
key={name}
className={styles[name]}
name={name}
isSortable={true}
{...otherProps}
>
<Icon
name={icons.TRENDING}
size={12}
/>
</VirtualTableHeaderCell>
);
}
if (name === 'isPopular') {
return (
<VirtualTableHeaderCell
key={name}
className={styles[name]}
name={name}
isSortable={true}
{...otherProps}
>
<Icon
name={icons.POPULAR}
size={12}
/>
</VirtualTableHeaderCell>
);
}
return ( return (
<VirtualTableHeaderCell <VirtualTableHeaderCell
key={name} key={name}

View File

@ -76,10 +76,12 @@
flex: 1 0 110px; flex: 1 0 110px;
} }
.isTrending,
.isPopular,
.isRecommendation { .isRecommendation {
composes: cell; composes: cell;
flex: 0 0 50px; flex: 0 0 30px;
} }
.actions { .actions {
@ -95,6 +97,11 @@
margin-top: 0; margin-top: 0;
} }
.statusIcon {
width: 20px !important;
text-align: center;
}
.externalLinks { .externalLinks {
margin-right: 0.5em; margin-right: 0.5em;
} }

View File

@ -12,7 +12,9 @@ interface CssExports {
'externalLinks': string; 'externalLinks': string;
'genres': string; 'genres': string;
'inCinemas': string; 'inCinemas': string;
'isPopular': string;
'isRecommendation': string; 'isRecommendation': string;
'isTrending': string;
'lists': string; 'lists': string;
'originalLanguage': string; 'originalLanguage': string;
'physicalRelease': string; 'physicalRelease': string;
@ -21,6 +23,7 @@ interface CssExports {
'runtime': string; 'runtime': string;
'sortTitle': string; 'sortTitle': string;
'status': string; 'status': string;
'statusIcon': string;
'studio': string; 'studio': string;
} }
export const cssExports: CssExports; export const cssExports: CssExports;

View File

@ -82,6 +82,8 @@ class DiscoverMovieRow extends Component {
isExisting, isExisting,
isExcluded, isExcluded,
isRecommendation, isRecommendation,
isTrending,
isPopular,
isSelected, isSelected,
lists, lists,
onSelectedChange onSelectedChange
@ -305,6 +307,7 @@ class DiscoverMovieRow extends Component {
{ {
isRecommendation ? isRecommendation ?
<Icon <Icon
className={styles.statusIcon}
name={icons.RECOMMENDED} name={icons.RECOMMENDED}
size={12} size={12}
title={translate('MovieIsRecommend')} title={translate('MovieIsRecommend')}
@ -315,6 +318,46 @@ class DiscoverMovieRow extends Component {
); );
} }
if (name === 'isTrending') {
return (
<VirtualTableRowCell
key={name}
className={styles[name]}
>
{
isTrending ?
<Icon
className={styles.statusIcon}
name={icons.TRENDING}
size={12}
title={translate('MovieIsTrending')}
/> :
null
}
</VirtualTableRowCell>
);
}
if (name === 'isPopular') {
return (
<VirtualTableRowCell
key={name}
className={styles[name]}
>
{
isPopular ?
<Icon
className={styles.statusIcon}
name={icons.POPULAR}
size={12}
title={translate('MovieIsPopular')}
/> :
null
}
</VirtualTableRowCell>
);
}
if (name === 'actions') { if (name === 'actions') {
return ( return (
<VirtualTableRowCell <VirtualTableRowCell
@ -404,6 +447,8 @@ DiscoverMovieRow.propTypes = {
isExcluded: PropTypes.bool.isRequired, isExcluded: PropTypes.bool.isRequired,
isSelected: PropTypes.bool, isSelected: PropTypes.bool,
isRecommendation: PropTypes.bool.isRequired, isRecommendation: PropTypes.bool.isRequired,
isPopular: PropTypes.bool.isRequired,
isTrending: PropTypes.bool.isRequired,
lists: PropTypes.arrayOf(PropTypes.number).isRequired, lists: PropTypes.arrayOf(PropTypes.number).isRequired,
onSelectedChange: PropTypes.func.isRequired onSelectedChange: PropTypes.func.isRequired
}; };

View File

@ -23,6 +23,7 @@ import {
import { import {
faArrowCircleLeft as fasArrowCircleLeft, faArrowCircleLeft as fasArrowCircleLeft,
faArrowCircleRight as fasArrowCircleRight, faArrowCircleRight as fasArrowCircleRight,
faArrowTrendUp as fasArrowTrendUp,
faAsterisk as fasAsterisk, faAsterisk as fasAsterisk,
faBackward as fasBackward, faBackward as fasBackward,
faBan as fasBan, faBan as fasBan,
@ -233,6 +234,7 @@ export const TAGS = fasTags;
export const TBA = fasQuestionCircle; export const TBA = fasQuestionCircle;
export const TEST = fasVial; export const TEST = fasVial;
export const TRANSLATE = fasLanguage; export const TRANSLATE = fasLanguage;
export const TRENDING = fasArrowTrendUp;
export const UNGROUP = farObjectUngroup; export const UNGROUP = farObjectUngroup;
export const UNKNOWN = fasQuestion; export const UNKNOWN = fasQuestion;
export const UNMONITORED = farBookmark; export const UNMONITORED = farBookmark;

View File

@ -88,6 +88,20 @@ export const defaultState = {
isVisible: true, isVisible: true,
isModifiable: false isModifiable: false
}, },
{
name: 'isTrending',
columnLabel: 'Trending',
isSortable: true,
isVisible: true,
isModifiable: false
},
{
name: 'isPopular',
columnLabel: 'Popular',
isSortable: true,
isVisible: true,
isModifiable: false
},
{ {
name: 'sortTitle', name: 'sortTitle',
label: () => translate('MovieTitle'), label: () => translate('MovieTitle'),
@ -267,6 +281,28 @@ export const defaultState = {
label: () => translate('All'), label: () => translate('All'),
filters: [] filters: []
}, },
{
key: 'popular',
label: 'Popular',
filters: [
{
key: 'isPopular',
value: true,
type: filterTypes.EQUAL
}
]
},
{
key: 'trending',
label: 'Trending',
filters: [
{
key: 'isTrending',
value: true,
type: filterTypes.EQUAL
}
]
},
{ {
key: 'newNotExcluded', key: 'newNotExcluded',
label: 'New Non-Excluded', label: 'New Non-Excluded',
@ -456,6 +492,18 @@ export const defaultState = {
label: 'Recommended', label: 'Recommended',
type: filterBuilderTypes.EXACT, type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.BOOL valueType: filterBuilderValueTypes.BOOL
},
{
name: 'isTrending',
label: 'Trending',
type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.BOOL
},
{
name: 'isPopular',
label: 'Popular',
type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.BOOL
} }
] ]
}; };

View File

@ -877,7 +877,9 @@
"MovieIsDownloading": "Movie is downloading", "MovieIsDownloading": "Movie is downloading",
"MovieIsMonitored": "Movie is monitored", "MovieIsMonitored": "Movie is monitored",
"MovieIsOnImportExclusionList": "Movie is on Import Exclusion List", "MovieIsOnImportExclusionList": "Movie is on Import Exclusion List",
"MovieIsPopular": "Movie is Popular on TMDb",
"MovieIsRecommend": "Movie is recommended based on recent addition", "MovieIsRecommend": "Movie is recommended based on recent addition",
"MovieIsTrending": "Movie is Trending on TMDb",
"MovieIsUnmonitored": "Movie is unmonitored", "MovieIsUnmonitored": "Movie is unmonitored",
"MovieMatchType": "Movie Match Type", "MovieMatchType": "Movie Match Type",
"MovieNaming": "Movie Naming", "MovieNaming": "Movie Naming",

View File

@ -12,6 +12,8 @@ public interface IProvideMovieInfo
Tuple<MovieMetadata, List<Credit>> GetMovieInfo(int tmdbId); Tuple<MovieMetadata, List<Credit>> GetMovieInfo(int tmdbId);
MovieCollection GetCollectionInfo(int tmdbId); MovieCollection GetCollectionInfo(int tmdbId);
List<MovieMetadata> GetBulkMovieInfo(List<int> tmdbIds); List<MovieMetadata> GetBulkMovieInfo(List<int> tmdbIds);
List<MovieMetadata> GetTrendingMovies();
List<MovieMetadata> GetPopularMovies();
HashSet<int> GetChangedMovies(DateTime startTime); HashSet<int> GetChangedMovies(DateTime startTime);
} }

View File

@ -70,6 +70,34 @@ public HashSet<int> GetChangedMovies(DateTime startTime)
return new HashSet<int>(response.Resource); return new HashSet<int>(response.Resource);
} }
public List<MovieMetadata> GetTrendingMovies()
{
var request = _radarrMetadata.Create()
.SetSegment("route", "list/tmdb/trending")
.Build();
request.AllowAutoRedirect = true;
request.SuppressHttpError = true;
var response = _httpClient.Get<List<MovieResource>>(request);
return response.Resource.DistinctBy(x => x.TmdbId).Select(MapMovie).ToList();
}
public List<MovieMetadata> GetPopularMovies()
{
var request = _radarrMetadata.Create()
.SetSegment("route", "list/tmdb/popular")
.Build();
request.AllowAutoRedirect = true;
request.SuppressHttpError = true;
var response = _httpClient.Get<List<MovieResource>>(request);
return response.Resource.DistinctBy(x => x.TmdbId).Select(MapMovie).ToList();
}
public Tuple<MovieMetadata, List<Credit>> GetMovieInfo(int tmdbId) public Tuple<MovieMetadata, List<Credit>> GetMovieInfo(int tmdbId)
{ {
var httpRequest = _radarrMetadata.Create() var httpRequest = _radarrMetadata.Create()

View File

@ -54,7 +54,7 @@ public ImportListMoviesController(IMovieService movieService,
} }
[HttpGet] [HttpGet]
public object GetDiscoverMovies(bool includeRecommendations = false) public object GetDiscoverMovies(bool includeRecommendations = false, bool includeTrending = false, bool includePopular = false)
{ {
var movieLanguage = (Language)_configService.MovieInfoLanguage; var movieLanguage = (Language)_configService.MovieInfoLanguage;
@ -77,6 +77,17 @@ public object GetDiscoverMovies(bool includeRecommendations = false)
realResults.ForEach(x => x.IsRecommendation = true); realResults.ForEach(x => x.IsRecommendation = true);
} }
// Add TMDB Trending
var trendingResults = _movieInfo.GetTrendingMovies();
realResults.AddRange(MapToResource(trendingResults.Select(m => new Movie { MovieMetadata = m }).Where(x => x != null), movieLanguage, true));
// Add TMDB Popular
var popularResults = _movieInfo.GetPopularMovies();
realResults.AddRange(MapToResource(popularResults.Select(m => new Movie { MovieMetadata = m }).Where(x => x != null), movieLanguage, false, true));
// 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();
realResults.AddRange(listMovies); realResults.AddRange(listMovies);
@ -92,6 +103,8 @@ public object GetDiscoverMovies(bool includeRecommendations = false)
movie.IsExcluded = listExclusions.Any(e => e.TmdbId == movie.TmdbId); movie.IsExcluded = listExclusions.Any(e => e.TmdbId == movie.TmdbId);
movie.IsExisting = existingTmdbIds.Any(e => e == movie.TmdbId); movie.IsExisting = existingTmdbIds.Any(e => e == movie.TmdbId);
movie.IsRecommendation = x.Any(m => m.IsRecommendation); movie.IsRecommendation = x.Any(m => m.IsRecommendation);
movie.IsPopular = x.Any(m => m.IsPopular);
movie.IsTrending = x.Any(m => m.IsTrending);
return movie; return movie;
}).ToList(); }).ToList();
@ -107,7 +120,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) private IEnumerable<ImportListMoviesResource> MapToResource(IEnumerable<Movie> movies, Language language, 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();
@ -127,6 +140,8 @@ 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.IsTrending = isTrending;
resource.IsPopular = isPopular;
yield return resource; yield return resource;
} }

View File

@ -42,6 +42,8 @@ public ImportListMoviesResource()
public MovieCollection Collection { get; set; } public MovieCollection Collection { get; set; }
public bool IsExcluded { get; set; } public bool IsExcluded { get; set; }
public bool IsExisting { get; set; } public bool IsExisting { get; set; }
public bool IsTrending { get; set; }
public bool IsPopular { get; set; }
public bool IsRecommendation { get; set; } public bool IsRecommendation { get; set; }
public HashSet<int> Lists { get; set; } public HashSet<int> Lists { get; set; }