1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-10-03 22:57:18 +02:00

New: Imdb Ratings

This commit is contained in:
Qstick 2020-10-13 00:14:15 -04:00
parent ec9a7f5c8e
commit 599f4907f4
29 changed files with 573 additions and 70 deletions

View File

@ -183,7 +183,7 @@ class AddNewMovieSearchResult extends Component {
<div>
<Label size={sizes.LARGE}>
<HeartRating
rating={ratings.value}
ratings={ratings}
iconSize={13}
/>
</Label>

View File

@ -1,4 +1,5 @@
.heart {
.image {
align-content: center;
margin-right: 5px;
color: $themeRed;
vertical-align: -0.125em;
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
.imdb {
align-content: center;
margin-right: 5px;
vertical-align: -0.125em;
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
.image {
align-content: center;
margin-right: 5px;
vertical-align: -0.125em;
}

View File

@ -0,0 +1,58 @@
import PropTypes from 'prop-types';
import React, { PureComponent } from 'react';
import styles from './RottenTomatoRating.css';
class RottenTomatoRating extends PureComponent {
//
// Render
render() {
const {
ratings,
hideIcon,
iconSize
} = this.props;
const rtRotten = 'data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNTYwIDU2MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNNDQ1LjE4NSA0NDQuNjg0Yy03OS4zNjkgNC4xNjctOTUuNTg3LTg2LjY1Mi0xMjYuNzI2LTg2LjAwNi0xMy4yNjguMjc5LTIzLjcyNiAxNC4xNTEtMTkuMTMzIDMwLjMyIDIuNTI1IDguODg4IDkuNTMgMjEuOTIzIDEzLjk0NCAzMC4wMTEgMTUuNTcgMjguNTQ0LTcuNDQ3IDYwLjg0NS0zNC4zODMgNjMuNTc3LTQ0Ljc2IDQuNTQtNjMuNDMzLTIxLjQyNi02Mi4yNzgtNDguMDA3IDEuMy0yOS44NCAyNi42LTYwLjMzMS42NS03My4zMDUtMjcuMTk0LTEzLjU5Ny00OS4zMDEgMzkuNTcyLTc1LjMyNSA1MS40MzktMjMuNTUzIDEwLjc0MS01Ni4yNDggMi40MTMtNjcuODcyLTIzLjc0MS04LjE2NC0xOC4zNzktNi42OC01My43NjggMjkuNjctNjcuMjcgMjIuNzA2LTguNDMzIDczLjMwNSAxMS4wMjkgNzUuOS0xMy42MjMgMi45OTItMjguNDE2LTUzLjE1NS0zMC44MTItNzAuMDYtMzcuNjI2LTI5LjkxMi0xMi4wNTUtNDcuNTY3LTM3Ljg1LTMzLjczNC02NS41MjIgMTAuMzc4LTIwLjc1NyA0MC45MTUtMjkuMjAzIDY0LjIyMy0yMC4xMSAyNy45MjIgMTAuODkyIDMyLjQwNCAzOS44NTMgNDYuNzEgNTEuODk3IDEyLjMyNCAxMC4zOCAyOS4xOSAxMS42OCA0MC4yMiA0LjU0MyA4LjEzNS01LjI2NSAxMC44NDMtMTYuODI4IDcuNzc0LTI3LjM5LTQuMDctMTQuMDIzLTE0Ljg3NS0yMi43NzMtMjUuNDE1LTMxLjM0Ni0xOC43NTgtMTUuMjQ5LTQ1LjI0LTI4LjM2LTI5LjIyMi02OS45ODMgMTMuMTMtMzQuMTEgNTEuNjQyLTM1LjM0IDUxLjY0Mi0zNS4zNCAxNS4zLTEuNzIgMjkuMDAyIDIuOSA0MC4xNjcgMTIuODc1IDE0LjkyNyAxMy4zMzUgMTcuODM0IDMxLjE2IDE1LjMzNiA1MC4xNzYtMi4yODMgMTcuMzU4LTguNDI2IDMyLjU2LTExLjYzIDQ5Ljc1OS0zLjcxNyAxOS45NjYgNi45NTQgNDAuMDg2IDI3LjI0OSA0MC44NjkgMjYuNjk0IDEuMDMxIDM0LjY5OC0xOS40ODYgMzcuOTY0LTMyLjQ5MiA0Ljc4Mi0xOS4wMjggMTEuMDU4LTM2LjY5NCAyOC43MTgtNDcuODIgMjUuMzQ2LTE1Ljk3IDYwLjU1Mi0xMi40NyA3Ni44ODYgMTguMjIyIDEyLjkyIDI0LjI4NCA4Ljc3MiA1Ny43MTUtMTEuMDQ3IDc1Ljk3LTguODkyIDguMTg4LTE5LjU4NCAxMS4wNzUtMzEuMTQ4IDExLjE1Ni0xNi41ODUuMTE3LTMzLjE2Mi0uMjktNDguNTU2IDcuNDcxLTEwLjQ4IDUuMjgxLTE1LjA0NyAxMy44ODgtMTUuMDQ1IDI1LjQyMyAwIDExLjI0MiA1Ljg1MyAxOC41ODUgMTUuMzM2IDIzLjM2MyAxNy44NiA5LjAwMyAzNy41NzcgMTAuODQzIDU2Ljg3MSAxNC4yMjIgMjcuOTggNC45IDUyLjU4MSAxNC43NTUgNjguMzc1IDQwLjcyLjE0Mi4yMjguMjguNDU4LjQxNS42OSAxOC4xMzkgMzAuNzQxLS44MzEgNzUuMDA1LTM2LjQ3NiA3Ni44NzgiIGZpbGw9IiMwQUM4NTUiLz48L3N2Zz4=';
const rtFresh = 'data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNTYwIDU2MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtNDc4LjI5IDI5Ni45OGMtMy45OS02My45NjYtMzYuNTItMTExLjgyLTg1LjQ2OC0xMzguNTggMC4yNzggMS41Ni0xLjEwOSAzLjUwOC0yLjY4OCAyLjgxOC0zMi4wMTYtMTQuMDA2LTg2LjMyOCAzMS4zMi0xMjQuMjggNy41ODQgMC4yODUgOC41MTktMS4zNzggNTAuMDcyLTU5LjkxNCA1Mi40ODMtMS4zODIgMC4wNTYtMi4xNDItMS4zNTUtMS4yNjgtMi4zNTQgNy44MjgtOC45MjkgMTUuNzMyLTMxLjUzNSA0LjM2Ny00My41ODYtMjQuMzM4IDIxLjgxLTM4LjQ3MiAzMC4wMTctODUuMTM4IDE5LjE4Ni0yOS44NzggMzEuMjQxLTQ2LjgwOSA3NC00My40ODUgMTI3LjI2IDYuNzggMTA4Ljc0IDEwOC42MyAxNzAuODkgMjExLjE5IDE2NC40OSAxMDIuNTYtNi4zOTUgMTkzLjQ3LTgwLjU3MiAxODYuNjgtMTg5LjMxIiBmaWxsPSIjRkEzMjBBIi8+PHBhdGggZD0iTTI5MS4zNzUgMTMyLjI5M2MyMS4wNzUtNS4wMjMgODEuNjkzLS40OSAxMDEuMTE0IDI1LjI3NCAxLjE2NiAxLjU0NS0uNDc1IDQuNDY4LTIuMzU1IDMuNjQ4LTMyLjAxNi0xNC4wMDYtODYuMzI4IDMxLjMyLTEyNC4yODIgNy41ODQuMjg1IDguNTE5LTEuMzc4IDUwLjA3Mi01OS45MTQgNTIuNDgzLTEuMzgyLjA1Ni0yLjE0Mi0xLjM1NS0xLjI2OC0yLjM1NCA3LjgyOC04LjkyOSAxNS43My0zMS41MzUgNC4zNjctNDMuNTg2LTI2LjUxMiAyMy43NTgtNDAuODg0IDMxLjM5Mi05OC40MjYgMTUuODM4LTEuODgzLS41MDgtMS4yNDEtMy41MzUuNzYyLTQuMjk4IDEwLjg3Ni00LjE1NyAzNS41MTUtMjIuMzYxIDU4LjgyNC0zMC4zODUgNC40MzgtMS41MjYgOC44NjItMi43MSAxMy4xOC0zLjQtMjUuNjY1LTIuMjkzLTM3LjIzNS01Ljg2Mi01My41NTktMy40LTEuNzg5LjI3LTMuMDA0LTEuODEzLTEuODk1LTMuMjQxIDIxLjk5NS0yOC4zMzIgNjIuNTEzLTM2Ljg4OCA4Ny41MTItMjEuODM3LTE1LjQxLTE5LjA5NC0yNy40OC0zNC4zMjEtMjcuNDgtMzQuMzIxbDI4LjYwMS0xNi4yNDZzMTEuODE3IDI2LjQgMjAuNDE0IDQ1LjYxNGMyMS4yNzUtMzEuNDM1IDYwLjg2LTM0LjMzNiA3Ny41ODUtMTIuMDMzLjk5MiAxLjMyNi0uMDQ1IDMuMjEtMS43MDIgMy4xNzEtMTMuNjEyLS4zMzEtMjEuMTA3IDEyLjA1LTIxLjY3NSAyMS40NjZsLjE5Ny4wMjMiIGZpbGw9IiMwMDkxMkQiLz48L3N2Zz4=';
const rating = ratings.rottenTomatoes;
let ratingString = '0%';
if (rating) {
ratingString = `${rating.value}%`;
}
return (
<span>
{
!hideIcon &&
<img
className={styles.image}
src={rating.value > 50 ? rtFresh : rtRotten}
style={{
height: `${iconSize}px`
}}
/>
}
{ratingString}
</span>
);
}
}
RottenTomatoRating.propTypes = {
ratings: PropTypes.object.isRequired,
iconSize: PropTypes.number.isRequired,
hideIcon: PropTypes.bool
};
RottenTomatoRating.defaultProps = {
iconSize: 14
};
export default RottenTomatoRating;

View File

@ -0,0 +1,5 @@
.tmdb {
align-content: center;
margin-right: 5px;
vertical-align: -0.125em;
}

View File

@ -0,0 +1,57 @@
import PropTypes from 'prop-types';
import React, { PureComponent } from 'react';
import styles from './TmdbRating.css';
class TmdbRating extends PureComponent {
//
// Render
render() {
const {
ratings,
hideIcon,
iconSize
} = this.props;
const tmdbImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxOTAuMjQgODEuNTIiPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iYSIgeTE9IjQwLjc2IiB4Mj0iMTkwLjI0IiB5Mj0iNDAuNzYiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiM5MGNlYTEiLz48c3RvcCBvZmZzZXQ9Ii41NiIgc3RvcC1jb2xvcj0iIzNjYmVjOSIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzAwYjNlNSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxwYXRoIGQ9Ik0xMDUuNjcgMzYuMDZoNjYuOWExNy42NyAxNy42NyAwIDAwMTcuNjctMTcuNjZBMTcuNjcgMTcuNjcgMCAwMDE3Mi41Ny43M2gtNjYuOUExNy42NyAxNy42NyAwIDAwODggMTguNGExNy42NyAxNy42NyAwIDAwMTcuNjcgMTcuNjZ6bS04OCA0NWg3Ni45YTE3LjY3IDE3LjY3IDAgMDAxNy42Ny0xNy42NiAxNy42NyAxNy42NyAwIDAwLTE3LjY3LTE3LjY3aC03Ni45QTE3LjY3IDE3LjY3IDAgMDAwIDYzLjRhMTcuNjcgMTcuNjcgMCAwMDE3LjY3IDE3LjY2em0tNy4yNi00NS42NGg3LjhWNi45MmgxMC4xVjBoLTI4djYuOWgxMC4xem0yOC4xIDBoNy44VjguMjVoLjFsOSAyNy4xNWg2bDkuMy0yNy4xNWguMVYzNS40aDcuOFYwSDY2Ljc2bC04LjIgMjMuMWgtLjFMNTAuMzEgMGgtMTEuOHptMTEzLjkyIDIwLjI1YTE1LjA3IDE1LjA3IDAgMDAtNC41Mi01LjUyIDE4LjU3IDE4LjU3IDAgMDAtNi42OC0zLjA4IDMzLjU0IDMzLjU0IDAgMDAtOC4wNy0xaC0xMS43djM1LjRoMTIuNzVhMjQuNTggMjQuNTggMCAwMDcuNTUtMS4xNSAxOS4zNCAxOS4zNCAwIDAwNi4zNS0zLjMyIDE2LjI3IDE2LjI3IDAgMDA0LjM3LTUuNSAxNi45MSAxNi45MSAwIDAwMS42My03LjU4IDE4LjUgMTguNSAwIDAwLTEuNjgtOC4yNXpNMTQ1IDY4LjZhOC44IDguOCAwIDAxLTIuNjQgMy40IDEwLjcgMTAuNyAwIDAxLTQgMS44MiAyMS41NyAyMS41NyAwIDAxLTUgLjU1aC00LjA1di0yMWg0LjZhMTcgMTcgMCAwMTQuNjcuNjMgMTEuNjYgMTEuNjYgMCAwMTMuODggMS44N0E5LjE0IDkuMTQgMCAwMTE0NSA1OWE5Ljg3IDkuODcgMCAwMTEgNC41MiAxMS44OSAxMS44OSAwIDAxLTEgNS4wOHptNDQuNjMtLjEzYTggOCAwIDAwLTEuNTgtMi42MiA4LjM4IDguMzggMCAwMC0yLjQyLTEuODUgMTAuMzEgMTAuMzEgMCAwMC0zLjE3LTF2LS4xYTkuMjIgOS4yMiAwIDAwNC40Mi0yLjgyIDcuNDMgNy40MyAwIDAwMS42OC01IDguNDIgOC40MiAwIDAwLTEuMTUtNC42NSA4LjA5IDguMDkgMCAwMC0zLTIuNzIgMTIuNTYgMTIuNTYgMCAwMC00LjE4LTEuMyAzMi44NCAzMi44NCAwIDAwLTQuNjItLjMzaC0xMy4ydjM1LjRoMTQuNWEyMi40MSAyMi40MSAwIDAwNC43Mi0uNSAxMy41MyAxMy41MyAwIDAwNC4yOC0xLjY1IDkuNDIgOS40MiAwIDAwMy4xLTMgOC41MiA4LjUyIDAgMDAxLjItNC42OCA5LjM5IDkuMzkgMCAwMC0uNTUtMy4xOHptLTE5LjQyLTE1Ljc1aDUuM2ExMCAxMCAwIDAxMS44NS4xOCA2LjE4IDYuMTggMCAwMTEuNy41NyAzLjM5IDMuMzkgMCAwMTEuMjIgMS4xMyAzLjIyIDMuMjIgMCAwMS40OCAxLjgyIDMuNjMgMy42MyAwIDAxLS40MyAxLjggMy40IDMuNCAwIDAxLTEuMTIgMS4yIDQuOTIgNC45MiAwIDAxLTEuNTguNjUgNy41MSA3LjUxIDAgMDEtMS43Ny4yaC01LjY1em0xMS43MiAyMGEzLjkgMy45IDAgMDEtMS4yMiAxLjMgNC42NCA0LjY0IDAgMDEtMS42OC43IDguMTggOC4xOCAwIDAxLTEuODIuMmgtN3YtOGg1LjlhMTUuMzUgMTUuMzUgMCAwMTIgLjE1IDguNDcgOC40NyAwIDAxMi4wNS41NSA0IDQgMCAwMTEuNTcgMS4xOCAzLjExIDMuMTEgMCAwMS42MyAyIDMuNzEgMy43MSAwIDAxLS40MyAxLjkyeiIgZmlsbD0idXJsKCNhKSIvPjwvc3ZnPg==';
const rating = ratings.tmdb;
let ratingString = '0%';
if (rating) {
ratingString = `${rating.value * 10}%`;
}
return (
<span title={`${rating.votes} votes`}>
{
!hideIcon &&
<img
className={styles.image}
src={tmdbImage}
style={{
height: `${iconSize}px`
}}
/>
}
{ratingString}
</span>
);
}
}
TmdbRating.propTypes = {
ratings: PropTypes.object.isRequired,
iconSize: PropTypes.number.isRequired,
hideIcon: PropTypes.bool
};
TmdbRating.defaultProps = {
iconSize: 14
};
export default TmdbRating;

View File

@ -73,7 +73,7 @@ function getInfoRowProps(row, props) {
return {
title: translate('Ratings'),
iconName: icons.HEART,
label: `${props.ratings.value * 10}%`
label: `${props.ratings.tmdb.value * 10}%`
};
}

View File

@ -112,7 +112,7 @@ function DiscoverMoviePosterInfo(props) {
return (
<div className={styles.info}>
<HeartRating
rating={ratings.value}
ratings={ratings}
/>
</div>
);
@ -129,7 +129,7 @@ DiscoverMoviePosterInfo.propTypes = {
digitalRelease: PropTypes.string,
physicalRelease: PropTypes.string,
runtime: PropTypes.number,
ratings: PropTypes.object,
ratings: PropTypes.arrayOf(PropTypes.object).isRequired,
sortKey: PropTypes.string.isRequired,
showRelativeDates: PropTypes.bool.isRequired,
shortDateFormat: PropTypes.string.isRequired,

View File

@ -246,7 +246,7 @@ class DiscoverMovieRow extends Component {
className={styles[name]}
>
<HeartRating
rating={ratings.value}
ratings={ratings}
/>
</VirtualTableRowCell>
);
@ -373,7 +373,7 @@ DiscoverMovieRow.propTypes = {
digitalRelease: PropTypes.string,
runtime: PropTypes.number,
genres: PropTypes.arrayOf(PropTypes.string).isRequired,
ratings: PropTypes.object.isRequired,
ratings: PropTypes.arrayOf(PropTypes.object).isRequired,
certification: PropTypes.string,
collection: PropTypes.object,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,

View File

@ -5,7 +5,7 @@
.header {
position: relative;
width: 100%;
height: 350px;
height: 375px;
}
.errorMessage {
@ -41,8 +41,8 @@
.poster {
flex-shrink: 0;
margin-right: 35px;
width: 200px;
height: 294px;
width: 217px;
height: 319px;
}
.info {

View File

@ -3,8 +3,8 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { Tab, TabList, TabPanel, Tabs } from 'react-tabs';
import TextTruncate from 'react-text-truncate';
import HeartRating from 'Components/HeartRating';
import Icon from 'Components/Icon';
import ImdbRating from 'Components/ImdbRating';
import InfoLabel from 'Components/InfoLabel';
import IconButton from 'Components/Link/IconButton';
import Marquee from 'Components/Marquee';
@ -16,6 +16,8 @@ import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
import RottenTomatoRating from 'Components/RottenTomatoRating';
import TmdbRating from 'Components/TmdbRating';
import Popover from 'Components/Tooltip/Popover';
import Tooltip from 'Components/Tooltip/Tooltip';
import { icons, kinds, sizes, tooltipPositions } from 'Helpers/Props';
@ -449,17 +451,6 @@ class MovieDetails extends Component {
</span>
}
{
!!ratings &&
<span className={styles.rating}>
<HeartRating
rating={ratings.value}
iconSize={20}
hideHeart={isSmallScreen}
/>
</span>
}
{
<span className={styles.links}>
<Tooltip
@ -501,6 +492,36 @@ class MovieDetails extends Component {
</div>
</div>
<div className={styles.details}>
{
!!ratings.tmdb &&
<span className={styles.rating}>
<TmdbRating
ratings={ratings}
iconSize={20}
/>
</span>
}
{
!!ratings.imdb &&
<span className={styles.rating}>
<ImdbRating
ratings={ratings}
iconSize={20}
/>
</span>
}
{
!!ratings.rottenTomatoes &&
<span className={styles.rating}>
<RottenTomatoRating
ratings={ratings}
iconSize={20}
/>
</span>
}
</div>
<div className={styles.detailsLabels}>
<InfoLabel
className={styles.detailsInfoLabel}

View File

@ -332,7 +332,7 @@ class MovieIndexRow extends Component {
className={styles[name]}
>
<HeartRating
rating={ratings.value}
ratings={ratings}
/>
</VirtualTableRowCell>
);

View File

@ -200,7 +200,7 @@ export const defaultState = {
ratings: function(item) {
const { ratings = {} } = item;
return ratings.value;
return ratings.tmdb? ratings.tmdb.value : 0;
}
},
@ -330,8 +330,23 @@ export const defaultState = {
valueType: filterBuilderValueTypes.MINIMUM_AVAILABILITY
},
{
name: 'ratings',
label: 'Rating',
name: 'tmdbRating',
label: translate('TmdbRating'),
type: filterBuilderTypes.NUMBER
},
{
name: 'tmdbVotes',
label: translate('TmdbVotes'),
type: filterBuilderTypes.NUMBER
},
{
name: 'imdbRating',
label: translate('ImdbRating'),
type: filterBuilderTypes.NUMBER
},
{
name: 'imdbVotes',
label: translate('ImdbVotes'),
type: filterBuilderTypes.NUMBER
},
{

View File

@ -131,10 +131,38 @@ export const filterPredicates = {
return dateFilterPredicate(item.digitalRelease, filterValue, type);
},
ratings: function(item, filterValue, type) {
tmdbRating: function(item, filterValue, type) {
const predicate = filterTypePredicates[type];
return predicate(item.ratings.value * 10, filterValue);
const rating = item.ratings.tmdb ? item.ratings.tmdb.value : 0;
return predicate(rating * 10, filterValue);
},
tmdbVotes: function(item, filterValue, type) {
const predicate = filterTypePredicates[type];
const rating = item.ratings.tmdb ? item.ratings.tmdb.votes : 0;
return predicate(rating, filterValue);
},
imdbRating: function(item, filterValue, type) {
const predicate = filterTypePredicates[type];
console.log(item.ratings);
const rating = item.ratings.imdb ? item.ratings.imdb.value : 0;
return predicate(rating, filterValue);
},
imdbVotes: function(item, filterValue, type) {
const predicate = filterTypePredicates[type];
const rating = item.ratings.imdb ? item.ratings.imdb.votes : 0;
return predicate(rating, filterValue);
},
qualityCutoffNotMet: function(item) {

View File

@ -209,7 +209,7 @@ export const defaultState = {
ratings: function(item) {
const { ratings = {} } = item;
return ratings.value;
return ratings.tmdb? ratings.tmdb.value : 0;
}
},
@ -357,8 +357,23 @@ export const defaultState = {
}
},
{
name: 'ratings',
label: translate('Ratings'),
name: 'tmdbRating',
label: translate('TmdbRating'),
type: filterBuilderTypes.NUMBER
},
{
name: 'tmdbVotes',
label: translate('TmdbVotes'),
type: filterBuilderTypes.NUMBER
},
{
name: 'imdbRating',
label: translate('ImdbRating'),
type: filterBuilderTypes.NUMBER
},
{
name: 'imdbVotes',
label: translate('ImdbVotes'),
type: filterBuilderTypes.NUMBER
},
{

View File

@ -0,0 +1,107 @@
using System.Collections.Generic;
using System.Data;
using System.Text.Json;
using System.Text.Json.Serialization;
using Dapper;
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(206)]
public class multiple_ratings_support : NzbDroneMigrationBase
{
private readonly JsonSerializerOptions _serializerSettings;
public multiple_ratings_support()
{
_serializerSettings = new JsonSerializerOptions
{
AllowTrailingCommas = true,
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
PropertyNameCaseInsensitive = true,
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
}
protected override void MainDbUpgrade()
{
Execute.Sql("UPDATE CustomFilters SET Filters = Replace(Filters, 'ratings', 'tmdbRating') WHERE Type = 'discoverMovie';");
Execute.Sql("UPDATE CustomFilters SET Filters = Replace(Filters, 'ratings', 'tmdbRating') WHERE Type = 'movieIndex';");
Execute.WithConnection((conn, tran) => FixRatings(conn, tran, "Movies"));
Execute.WithConnection((conn, tran) => FixRatings(conn, tran, "ImportListMovies"));
}
private void FixRatings(IDbConnection conn, IDbTransaction tran, string table)
{
var rows = conn.Query<Movie205>($"SELECT Id, Ratings FROM {table}");
var corrected = new List<Movie206>();
foreach (var row in rows)
{
var oldRatings = JsonSerializer.Deserialize<Ratings205>(row.Ratings, _serializerSettings);
var newRatings = new Ratings206
{
Tmdb = new RatingChild206
{
Votes = oldRatings.Votes,
Value = oldRatings.Value,
Type = RatingType206.User
}
};
corrected.Add(new Movie206
{
Id = row.Id,
Ratings = JsonSerializer.Serialize(newRatings, _serializerSettings)
});
}
var updateSql = $"UPDATE {table} SET Ratings = @Ratings WHERE Id = @Id";
conn.Execute(updateSql, corrected, transaction: tran);
}
private class Movie205
{
public int Id { get; set; }
public string Ratings { get; set; }
}
private class Ratings205
{
public int Votes { get; set; }
public decimal Value { get; set; }
}
private class Movie206
{
public int Id { get; set; }
public string Ratings { get; set; }
}
private class Ratings206
{
public RatingChild206 Tmdb { get; set; }
public RatingChild206 Imdb { get; set; }
public RatingChild206 Metacritic { get; set; }
public RatingChild206 RottenTomatoes { get; set; }
}
private class RatingChild206
{
public int Votes { get; set; }
public decimal Value { get; set; }
public RatingType206 Type { get; set; }
}
private enum RatingType206
{
User
}
}
}

View File

@ -179,6 +179,7 @@ private static void RegisterMappers()
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<string>>());
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<ParsedMovieInfo>(new QualityIntConverter(), new LanguageIntConverter()));
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<ReleaseInfo>());
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<Ratings>());
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<MovieTranslation>>());
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<HashSet<int>>());
SqlMapper.AddTypeHandler(new OsPathConverter());

View File

@ -75,7 +75,7 @@ public override MetadataFileResult MovieMetadata(Movie movie, MovieFile movieFil
movieElement.Add(new XElement("Overview", movie.Overview));
movieElement.Add(new XElement("LocalTitle", movie.Title));
movieElement.Add(new XElement("Rating", movie.Ratings.Value));
movieElement.Add(new XElement("Rating", movie.Ratings.Tmdb?.Value ?? 0));
movieElement.Add(new XElement("ProductionYear", movie.Year));
movieElement.Add(new XElement("RunningTime", movie.Runtime));
movieElement.Add(new XElement("IMDB", movie.ImdbId));

View File

@ -154,19 +154,32 @@ public override MetadataFileResult MovieMetadata(Movie movie, MovieFile movieFil
details.Add(new XElement("sorttitle", movie.SortTitle));
if (movie.Ratings != null && movie.Ratings.Votes > 0)
if (movie.Ratings.Tmdb?.Votes > 0 || movie.Ratings.Imdb?.Votes > 0)
{
var setRating = new XElement("ratings");
var setRatethemoviedb = new XElement("rating", new XAttribute("name", "themoviedb"), new XAttribute("max", "10"), new XAttribute("default", "true"));
setRatethemoviedb.Add(new XElement("value", movie.Ratings.Value));
setRatethemoviedb.Add(new XElement("votes", movie.Ratings.Votes));
setRating.Add(setRatethemoviedb);
if (movie.Ratings.Tmdb?.Votes > 0)
{
var setRatethemoviedb = new XElement("rating", new XAttribute("name", "themoviedb"), new XAttribute("max", "10"), new XAttribute("default", "true"));
setRatethemoviedb.Add(new XElement("value", movie.Ratings.Tmdb.Value));
setRatethemoviedb.Add(new XElement("votes", movie.Ratings.Tmdb.Votes));
setRating.Add(setRatethemoviedb);
}
if (movie.Ratings.Imdb?.Votes > 0)
{
var setRateImdb = new XElement("rating", new XAttribute("name", "imdb"), new XAttribute("max", "10"));
setRateImdb.Add(new XElement("value", movie.Ratings.Imdb.Value));
setRateImdb.Add(new XElement("votes", movie.Ratings.Imdb.Votes));
setRating.Add(setRateImdb);
}
details.Add(setRating);
}
if (movie.Ratings != null && movie.Ratings.Votes > 0)
if (movie.Ratings?.Tmdb?.Votes > 0)
{
details.Add(new XElement("rating", movie.Ratings.Value));
details.Add(new XElement("rating", movie.Ratings.Tmdb.Value));
}
details.Add(new XElement("userrating"));

View File

@ -13,6 +13,7 @@ public ImportListMovie()
Images = new List<MediaCover.MediaCover>();
Genres = new List<string>();
Translations = new List<MovieTranslation>();
Ratings = new Ratings();
}
public int TmdbId { get; set; }

View File

@ -226,6 +226,10 @@
"DetailedProgressBar": "Detailed Progress Bar",
"DetailedProgressBarHelpText": "Show text on progress bar",
"Details": "Details",
"TmdbRating": "TMDb Rating",
"TmdbVotes": "TMDb Votes",
"ImdbRating": "IMDb Rating",
"ImdbVotes": "IMDb Votes",
"DigitalRelease": "Digital Release",
"Disabled": "Disabled",
"Discord": "Discord",

View File

@ -11,7 +11,10 @@ public class MovieResource
public string Title { get; set; }
public string OriginalTitle { get; set; }
public string TitleSlug { get; set; }
public List<RatingResource> Ratings { get; set; }
//Depricated but left in place until cache fills new object (MovieRatings)
public List<RatingItem> Ratings { get; set; }
public RatingResource MovieRatings { get; set; }
public int? Runtime { get; set; }
public List<ImageResource> Images { get; set; }
public List<string> Genres { get; set; }

View File

@ -1,6 +1,14 @@
namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
{
public class RatingResource
{
public RatingItem Tmdb { get; set; }
public RatingItem Imdb { get; set; }
public RatingItem Metacritic { get; set; }
public RatingItem RottenTomatoes { get; set; }
}
public class RatingItem
{
public int Count { get; set; }
public decimal Value { get; set; }

View File

@ -202,10 +202,21 @@ public Movie MapMovie(MovieResource resource)
var certificationCountry = _configService.CertificationCountry.ToString();
movie.Certification = resource.Certifications.FirstOrDefault(m => m.Country == certificationCountry)?.Certification;
movie.Ratings = resource.Ratings.Select(MapRatings).FirstOrDefault() ?? new Ratings();
movie.Ratings = MapRatings(resource.MovieRatings) ?? new Ratings();
movie.Genres = resource.Genres;
movie.Recommendations = resource.Recommendations?.Select(r => r.TmdbId).ToList() ?? new List<int>();
//Workaround due to metadata change until cache cleans up
if (movie.Ratings.Tmdb == null)
{
var tmdbRating = resource.Ratings.FirstOrDefault();
movie.Ratings.Tmdb = new RatingChild
{
Votes = tmdbRating.Count,
Value = tmdbRating.Value
};
}
var now = DateTime.Now;
movie.Status = MovieStatusType.Announced;
@ -512,18 +523,56 @@ private static MovieTranslation MapTranslation(TranslationResource arg)
return newAlternativeTitle;
}
private static Ratings MapRatings(RatingResource rating)
private static Ratings MapRatings(RatingResource ratings)
{
if (rating == null)
if (ratings == null)
{
return new Ratings();
}
return new Ratings
var mappedRatings = new Ratings();
if (ratings.Tmdb != null)
{
Votes = rating.Count,
Value = rating.Value
};
mappedRatings.Tmdb = new RatingChild
{
Type = (RatingType)Enum.Parse(typeof(RatingType), ratings.Tmdb.Type),
Value = ratings.Tmdb.Value,
Votes = ratings.Tmdb.Count
};
}
if (ratings.Imdb != null)
{
mappedRatings.Imdb = new RatingChild
{
Type = (RatingType)Enum.Parse(typeof(RatingType), ratings.Imdb.Type),
Value = ratings.Imdb.Value,
Votes = ratings.Imdb.Count
};
}
if (ratings.Metacritic != null)
{
mappedRatings.Metacritic = new RatingChild
{
Type = (RatingType)Enum.Parse(typeof(RatingType), ratings.Metacritic.Type),
Value = ratings.Metacritic.Value,
Votes = ratings.Metacritic.Count
};
}
if (ratings.RottenTomatoes != null)
{
mappedRatings.RottenTomatoes = new RatingChild
{
Type = (RatingType)Enum.Parse(typeof(RatingType), ratings.RottenTomatoes.Type),
Value = ratings.RottenTomatoes.Value,
Votes = ratings.RottenTomatoes.Count
};
}
return mappedRatings;
}
private static MediaCover.MediaCover MapImage(ImageResource arg)

View File

@ -1,10 +1,25 @@
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Movies
{
public class Ratings : IEmbeddedDocument
{
public RatingChild Imdb { get; set; }
public RatingChild Tmdb { get; set; }
public RatingChild Metacritic { get; set; }
public RatingChild RottenTomatoes { get; set; }
}
public class RatingChild
{
public int Votes { get; set; }
public decimal Value { get; set; }
public RatingType Type { get; set; }
}
public enum RatingType
{
User,
Critic
}
}

View File

@ -70,7 +70,7 @@ public override void OnGrab(GrabMessage message)
break;
case DiscordGrabFieldType.Rating:
discordField.Name = "Rating";
discordField.Value = message.Movie.Ratings.Value.ToString();
discordField.Value = message.Movie.Ratings.Tmdb?.Value.ToString() ?? string.Empty;
break;
case DiscordGrabFieldType.Genres:
discordField.Name = "Genres";
@ -157,7 +157,7 @@ public override void OnDownload(DownloadMessage message)
break;
case DiscordImportFieldType.Rating:
discordField.Name = "Rating";
discordField.Value = message.Movie.Ratings.Value.ToString();
discordField.Value = message.Movie.Ratings.Tmdb?.Value.ToString() ?? string.Empty;
break;
case DiscordImportFieldType.Genres:
discordField.Name = "Genres";