mirror of
https://github.com/fallenbagel/jellyseerr.git
synced 2026-01-08 15:48:08 -05:00
Merge remote-tracking branch 'overseerr/develop' into develop
This commit is contained in:
@@ -1,3 +1,33 @@
|
||||
import RTAudFresh from '@app/assets/rt_aud_fresh.svg';
|
||||
import RTAudRotten from '@app/assets/rt_aud_rotten.svg';
|
||||
import RTFresh from '@app/assets/rt_fresh.svg';
|
||||
import RTRotten from '@app/assets/rt_rotten.svg';
|
||||
import TmdbLogo from '@app/assets/tmdb_logo.svg';
|
||||
import Badge from '@app/components/Common/Badge';
|
||||
import Button from '@app/components/Common/Button';
|
||||
import CachedImage from '@app/components/Common/CachedImage';
|
||||
import LoadingSpinner from '@app/components/Common/LoadingSpinner';
|
||||
import PageTitle from '@app/components/Common/PageTitle';
|
||||
import type { PlayButtonLink } from '@app/components/Common/PlayButton';
|
||||
import PlayButton from '@app/components/Common/PlayButton';
|
||||
import Tooltip from '@app/components/Common/Tooltip';
|
||||
import ExternalLinkBlock from '@app/components/ExternalLinkBlock';
|
||||
import IssueModal from '@app/components/IssueModal';
|
||||
import ManageSlideOver from '@app/components/ManageSlideOver';
|
||||
import MediaSlider from '@app/components/MediaSlider';
|
||||
import PersonCard from '@app/components/PersonCard';
|
||||
import RequestButton from '@app/components/RequestButton';
|
||||
import RequestModal from '@app/components/RequestModal';
|
||||
import Slider from '@app/components/Slider';
|
||||
import StatusBadge from '@app/components/StatusBadge';
|
||||
import Season from '@app/components/TvDetails/Season';
|
||||
import useLocale from '@app/hooks/useLocale';
|
||||
import useSettings from '@app/hooks/useSettings';
|
||||
import { Permission, useUser } from '@app/hooks/useUser';
|
||||
import globalMessages from '@app/i18n/globalMessages';
|
||||
import Error from '@app/pages/_error';
|
||||
import { sortCrewPriority } from '@app/utils/creditHelpers';
|
||||
import { Disclosure, Transition } from '@headlessui/react';
|
||||
import {
|
||||
ArrowCircleRightIcon,
|
||||
CogIcon,
|
||||
@@ -5,46 +35,22 @@ import {
|
||||
FilmIcon,
|
||||
PlayIcon,
|
||||
} from '@heroicons/react/outline';
|
||||
import { ChevronUpIcon } from '@heroicons/react/solid';
|
||||
import type { RTRating } from '@server/api/rottentomatoes';
|
||||
import { ANIME_KEYWORD_ID } from '@server/api/themoviedb/constants';
|
||||
import { IssueStatus } from '@server/constants/issue';
|
||||
import { MediaRequestStatus, MediaStatus } from '@server/constants/media';
|
||||
import { MediaServerType } from '@server/constants/server';
|
||||
import type { Crew } from '@server/models/common';
|
||||
import type { TvDetails as TvDetailsType } from '@server/models/Tv';
|
||||
import { hasFlag } from 'country-flag-icons';
|
||||
import 'country-flag-icons/3x2/flags.css';
|
||||
import getConfig from 'next/config';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR from 'swr';
|
||||
import type { RTRating } from '../../../server/api/rottentomatoes';
|
||||
import { ANIME_KEYWORD_ID } from '../../../server/api/themoviedb/constants';
|
||||
import { IssueStatus } from '../../../server/constants/issue';
|
||||
import { MediaStatus } from '../../../server/constants/media';
|
||||
import { MediaServerType } from '../../../server/constants/server';
|
||||
import { Crew } from '../../../server/models/common';
|
||||
import { TvDetails as TvDetailsType } from '../../../server/models/Tv';
|
||||
import RTAudFresh from '../../assets/rt_aud_fresh.svg';
|
||||
import RTAudRotten from '../../assets/rt_aud_rotten.svg';
|
||||
import RTFresh from '../../assets/rt_fresh.svg';
|
||||
import RTRotten from '../../assets/rt_rotten.svg';
|
||||
import TmdbLogo from '../../assets/tmdb_logo.svg';
|
||||
import useLocale from '../../hooks/useLocale';
|
||||
import useSettings from '../../hooks/useSettings';
|
||||
import { Permission, useUser } from '../../hooks/useUser';
|
||||
import globalMessages from '../../i18n/globalMessages';
|
||||
import Error from '../../pages/_error';
|
||||
import { sortCrewPriority } from '../../utils/creditHelpers';
|
||||
import Button from '../Common/Button';
|
||||
import CachedImage from '../Common/CachedImage';
|
||||
import LoadingSpinner from '../Common/LoadingSpinner';
|
||||
import PageTitle from '../Common/PageTitle';
|
||||
import PlayButton, { PlayButtonLink } from '../Common/PlayButton';
|
||||
import ExternalLinkBlock from '../ExternalLinkBlock';
|
||||
import IssueModal from '../IssueModal';
|
||||
import ManageSlideOver from '../ManageSlideOver';
|
||||
import MediaSlider from '../MediaSlider';
|
||||
import PersonCard from '../PersonCard';
|
||||
import RequestButton from '../RequestButton';
|
||||
import RequestModal from '../RequestModal';
|
||||
import Slider from '../Slider';
|
||||
import StatusBadge from '../StatusBadge';
|
||||
import getConfig from 'next/config';
|
||||
|
||||
const messages = defineMessages({
|
||||
firstAirDate: 'First Air Date',
|
||||
@@ -69,13 +75,22 @@ const messages = defineMessages({
|
||||
streamingproviders: 'Currently Streaming On',
|
||||
productioncountries:
|
||||
'Production {countryCount, plural, one {Country} other {Countries}}',
|
||||
reportissue: 'Report an Issue',
|
||||
manageseries: 'Manage Series',
|
||||
seasonstitle: 'Seasons',
|
||||
episodeCount: '{episodeCount, plural, one {# Episode} other {# Episodes}}',
|
||||
seasonnumber: 'Season {seasonNumber}',
|
||||
status4k: '4K {status}',
|
||||
rtcriticsscore: 'Rotten Tomatoes Tomatometer',
|
||||
rtaudiencescore: 'Rotten Tomatoes Audience Score',
|
||||
tmdbuserscore: 'TMDB User Score',
|
||||
});
|
||||
|
||||
interface TvDetailsProps {
|
||||
tv?: TvDetailsType;
|
||||
}
|
||||
|
||||
const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
||||
const TvDetails = ({ tv }: TvDetailsProps) => {
|
||||
const settings = useSettings();
|
||||
const { user, hasPermission } = useUser();
|
||||
const router = useRouter();
|
||||
@@ -109,6 +124,32 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
||||
setShowManager(router.query.manage == '1' ? true : false);
|
||||
}, [router.query.manage]);
|
||||
|
||||
const [plexUrl, setPlexUrl] = useState(data?.mediaInfo?.mediaUrl);
|
||||
const [plexUrl4k, setPlexUrl4k] = useState(data?.mediaInfo?.mediaUrl4k);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
if (
|
||||
settings.currentSettings.mediaServerType === MediaServerType.PLEX &&
|
||||
(/iPad|iPhone|iPod/.test(navigator.userAgent) ||
|
||||
(navigator.userAgent === 'MacIntel' && navigator.maxTouchPoints > 1))
|
||||
) {
|
||||
setPlexUrl(data.mediaInfo?.iOSPlexUrl);
|
||||
setPlexUrl4k(data.mediaInfo?.iOSPlexUrl4k);
|
||||
} else {
|
||||
setPlexUrl(data.mediaInfo?.mediaUrl);
|
||||
setPlexUrl4k(data.mediaInfo?.mediaUrl4k);
|
||||
}
|
||||
}
|
||||
}, [
|
||||
data,
|
||||
data?.mediaInfo?.iOSPlexUrl,
|
||||
data?.mediaInfo?.iOSPlexUrl4k,
|
||||
data?.mediaInfo?.mediaUrl,
|
||||
data?.mediaInfo?.mediaUrl4k,
|
||||
settings.currentSettings.mediaServerType,
|
||||
]);
|
||||
|
||||
if (!data && !error) {
|
||||
return <LoadingSpinner />;
|
||||
}
|
||||
@@ -120,28 +161,28 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
||||
const mediaLinks: PlayButtonLink[] = [];
|
||||
|
||||
if (
|
||||
data.mediaInfo?.mediaUrl &&
|
||||
plexUrl &&
|
||||
hasPermission([Permission.REQUEST, Permission.REQUEST_TV], {
|
||||
type: 'or',
|
||||
})
|
||||
) {
|
||||
mediaLinks.push({
|
||||
text: getAvalaibleMediaServerName(),
|
||||
url: data.mediaInfo?.mediaUrl,
|
||||
url: plexUrl,
|
||||
svg: <PlayIcon />,
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
settings.currentSettings.series4kEnabled &&
|
||||
data.mediaInfo?.mediaUrl4k &&
|
||||
plexUrl4k &&
|
||||
hasPermission([Permission.REQUEST_4K, Permission.REQUEST_4K_TV], {
|
||||
type: 'or',
|
||||
})
|
||||
) {
|
||||
mediaLinks.push({
|
||||
text: getAvalaible4kMediaServerName(),
|
||||
url: data.mediaInfo?.mediaUrl4k,
|
||||
url: plexUrl4k,
|
||||
svg: <PlayIcon />,
|
||||
});
|
||||
}
|
||||
@@ -208,7 +249,9 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
||||
seasonCount <=
|
||||
(
|
||||
data.mediaInfo?.seasons.filter(
|
||||
(season) => season.status === MediaStatus.AVAILABLE
|
||||
(season) =>
|
||||
season.status === MediaStatus.AVAILABLE ||
|
||||
season.status === MediaStatus.PARTIALLY_AVAILABLE
|
||||
) ?? []
|
||||
).length;
|
||||
|
||||
@@ -216,7 +259,9 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
||||
seasonCount <=
|
||||
(
|
||||
data.mediaInfo?.seasons.filter(
|
||||
(season) => season.status4k === MediaStatus.AVAILABLE
|
||||
(season) =>
|
||||
season.status4k === MediaStatus.AVAILABLE ||
|
||||
season.status4k === MediaStatus.PARTIALLY_AVAILABLE
|
||||
) ?? []
|
||||
).length;
|
||||
|
||||
@@ -325,7 +370,8 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
||||
inProgress={(data.mediaInfo?.downloadStatus ?? []).length > 0}
|
||||
tmdbId={data.mediaInfo?.tmdbId}
|
||||
mediaType="tv"
|
||||
plexUrl={data.mediaInfo?.mediaUrl}
|
||||
plexUrl={plexUrl}
|
||||
serviceUrl={data.mediaInfo?.serviceUrl}
|
||||
/>
|
||||
{settings.currentSettings.series4kEnabled &&
|
||||
hasPermission(
|
||||
@@ -346,11 +392,12 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
||||
}
|
||||
tmdbId={data.mediaInfo?.tmdbId}
|
||||
mediaType="tv"
|
||||
plexUrl={data.mediaInfo?.mediaUrl4k}
|
||||
plexUrl={plexUrl4k}
|
||||
serviceUrl={data.mediaInfo?.serviceUrl4k}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<h1>
|
||||
<h1 data-testid="media-title">
|
||||
{data.name}{' '}
|
||||
{data.firstAirDate && (
|
||||
<span className="media-year">
|
||||
@@ -396,38 +443,42 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
||||
type: 'or',
|
||||
}
|
||||
) && (
|
||||
<Button
|
||||
buttonType="warning"
|
||||
className="ml-2 first:ml-0"
|
||||
onClick={() => setShowIssueModal(true)}
|
||||
>
|
||||
<ExclamationIcon className="w-5" />
|
||||
</Button>
|
||||
<Tooltip content={intl.formatMessage(messages.reportissue)}>
|
||||
<Button
|
||||
buttonType="warning"
|
||||
onClick={() => setShowIssueModal(true)}
|
||||
className="ml-2 first:ml-0"
|
||||
>
|
||||
<ExclamationIcon />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
{hasPermission(Permission.MANAGE_REQUESTS) && data.mediaInfo && (
|
||||
<Button
|
||||
buttonType="default"
|
||||
className="relative ml-2 first:ml-0"
|
||||
onClick={() => setShowManager(true)}
|
||||
>
|
||||
<CogIcon className="!mr-0" />
|
||||
{hasPermission(
|
||||
[Permission.MANAGE_ISSUES, Permission.VIEW_ISSUES],
|
||||
{
|
||||
type: 'or',
|
||||
}
|
||||
) &&
|
||||
(
|
||||
data.mediaInfo?.issues.filter(
|
||||
(issue) => issue.status === IssueStatus.OPEN
|
||||
) ?? []
|
||||
).length > 0 && (
|
||||
<>
|
||||
<div className="absolute -right-1 -top-1 h-3 w-3 rounded-full bg-red-600" />
|
||||
<div className="absolute -right-1 -top-1 h-3 w-3 animate-ping rounded-full bg-red-600" />
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
<Tooltip content={intl.formatMessage(messages.manageseries)}>
|
||||
<Button
|
||||
buttonType="ghost"
|
||||
onClick={() => setShowManager(true)}
|
||||
className="relative ml-2 first:ml-0"
|
||||
>
|
||||
<CogIcon className="!mr-0" />
|
||||
{hasPermission(
|
||||
[Permission.MANAGE_ISSUES, Permission.VIEW_ISSUES],
|
||||
{
|
||||
type: 'or',
|
||||
}
|
||||
) &&
|
||||
(
|
||||
data.mediaInfo?.issues.filter(
|
||||
(issue) => issue.status === IssueStatus.OPEN
|
||||
) ?? []
|
||||
).length > 0 && (
|
||||
<>
|
||||
<div className="absolute -right-1 -top-1 h-3 w-3 rounded-full bg-red-600" />
|
||||
<div className="absolute -right-1 -top-1 h-3 w-3 animate-ping rounded-full bg-red-600" />
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -476,6 +527,174 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<h2 className="py-4">{intl.formatMessage(messages.seasonstitle)}</h2>
|
||||
<div className="flex w-full flex-col space-y-2">
|
||||
{data.seasons
|
||||
.slice()
|
||||
.reverse()
|
||||
.filter((season) => season.seasonNumber !== 0)
|
||||
.map((season) => {
|
||||
const show4k =
|
||||
settings.currentSettings.series4kEnabled &&
|
||||
hasPermission(
|
||||
[
|
||||
Permission.MANAGE_REQUESTS,
|
||||
Permission.REQUEST_4K,
|
||||
Permission.REQUEST_4K_TV,
|
||||
],
|
||||
{
|
||||
type: 'or',
|
||||
}
|
||||
);
|
||||
const mSeason = (data.mediaInfo?.seasons ?? []).find(
|
||||
(s) =>
|
||||
season.seasonNumber === s.seasonNumber &&
|
||||
s.status !== MediaStatus.UNKNOWN
|
||||
);
|
||||
const mSeason4k = (data.mediaInfo?.seasons ?? []).find(
|
||||
(s) =>
|
||||
season.seasonNumber === s.seasonNumber &&
|
||||
s.status4k !== MediaStatus.UNKNOWN
|
||||
);
|
||||
const request = (data.mediaInfo?.requests ?? []).find(
|
||||
(r) =>
|
||||
!!r.seasons.find(
|
||||
(s) => s.seasonNumber === season.seasonNumber
|
||||
) && !r.is4k
|
||||
);
|
||||
const request4k = (data.mediaInfo?.requests ?? []).find(
|
||||
(r) =>
|
||||
!!r.seasons.find(
|
||||
(s) => s.seasonNumber === season.seasonNumber
|
||||
) && r.is4k
|
||||
);
|
||||
|
||||
return (
|
||||
<Disclosure key={`season-discoslure-${season.seasonNumber}`}>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Disclosure.Button
|
||||
className={`mt-2 flex w-full items-center justify-between space-x-2 border-gray-700 bg-gray-800 px-4 py-2 text-gray-200 ${
|
||||
open
|
||||
? 'rounded-t-md border-t border-l border-r'
|
||||
: 'rounded-md border'
|
||||
}`}
|
||||
>
|
||||
<div className="flex flex-1 items-center space-x-2 text-lg">
|
||||
<span>
|
||||
{intl.formatMessage(messages.seasonnumber, {
|
||||
seasonNumber: season.seasonNumber,
|
||||
})}
|
||||
</span>
|
||||
<Badge badgeType="dark">
|
||||
{intl.formatMessage(messages.episodeCount, {
|
||||
episodeCount: season.episodeCount,
|
||||
})}
|
||||
</Badge>
|
||||
</div>
|
||||
{((!mSeason &&
|
||||
request?.status === MediaRequestStatus.APPROVED) ||
|
||||
mSeason?.status === MediaStatus.PROCESSING) && (
|
||||
<Badge badgeType="primary">
|
||||
{intl.formatMessage(globalMessages.requested)}
|
||||
</Badge>
|
||||
)}
|
||||
{((!mSeason &&
|
||||
request?.status === MediaRequestStatus.PENDING) ||
|
||||
mSeason?.status === MediaStatus.PENDING) && (
|
||||
<Badge badgeType="warning">
|
||||
{intl.formatMessage(globalMessages.pending)}
|
||||
</Badge>
|
||||
)}
|
||||
{mSeason?.status ===
|
||||
MediaStatus.PARTIALLY_AVAILABLE && (
|
||||
<Badge badgeType="success">
|
||||
{intl.formatMessage(
|
||||
globalMessages.partiallyavailable
|
||||
)}
|
||||
</Badge>
|
||||
)}
|
||||
{mSeason?.status === MediaStatus.AVAILABLE && (
|
||||
<Badge badgeType="success">
|
||||
{intl.formatMessage(globalMessages.available)}
|
||||
</Badge>
|
||||
)}
|
||||
{((!mSeason4k &&
|
||||
request4k?.status ===
|
||||
MediaRequestStatus.APPROVED) ||
|
||||
mSeason4k?.status4k === MediaStatus.PROCESSING) &&
|
||||
show4k && (
|
||||
<Badge badgeType="primary">
|
||||
{intl.formatMessage(messages.status4k, {
|
||||
status: intl.formatMessage(
|
||||
globalMessages.requested
|
||||
),
|
||||
})}
|
||||
</Badge>
|
||||
)}
|
||||
{((!mSeason4k &&
|
||||
request4k?.status === MediaRequestStatus.PENDING) ||
|
||||
mSeason?.status4k === MediaStatus.PENDING) &&
|
||||
show4k && (
|
||||
<Badge badgeType="warning">
|
||||
{intl.formatMessage(messages.status4k, {
|
||||
status: intl.formatMessage(
|
||||
globalMessages.pending
|
||||
),
|
||||
})}
|
||||
</Badge>
|
||||
)}
|
||||
{mSeason4k?.status4k ===
|
||||
MediaStatus.PARTIALLY_AVAILABLE &&
|
||||
show4k && (
|
||||
<Badge badgeType="success">
|
||||
{intl.formatMessage(messages.status4k, {
|
||||
status: intl.formatMessage(
|
||||
globalMessages.partiallyavailable
|
||||
),
|
||||
})}
|
||||
</Badge>
|
||||
)}
|
||||
{mSeason4k?.status4k === MediaStatus.AVAILABLE &&
|
||||
show4k && (
|
||||
<Badge badgeType="success">
|
||||
{intl.formatMessage(messages.status4k, {
|
||||
status: intl.formatMessage(
|
||||
globalMessages.available
|
||||
),
|
||||
})}
|
||||
</Badge>
|
||||
)}
|
||||
<ChevronUpIcon
|
||||
className={`${
|
||||
open ? 'rotate-180 transform' : ''
|
||||
} h-6 w-6 text-gray-500`}
|
||||
/>
|
||||
</Disclosure.Button>
|
||||
<Transition
|
||||
show={open}
|
||||
enter="transition duration-100 ease-out"
|
||||
enterFrom="transform opacity-0"
|
||||
enterTo="transform opacity-100"
|
||||
leave="transition duration-75 ease-out"
|
||||
leaveFrom="transform opacity-100"
|
||||
leaveTo="transform opacity-0"
|
||||
// Not sure why this transition is adding a margin without this here
|
||||
style={{ margin: '0px' }}
|
||||
>
|
||||
<Disclosure.Panel className="w-full rounded-b-md border-b border-l border-r border-gray-700 px-4 pb-2">
|
||||
<Season
|
||||
tvId={data.id}
|
||||
seasonNumber={season.seasonNumber}
|
||||
/>
|
||||
</Disclosure.Panel>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="media-overview-right">
|
||||
<div className="media-facts">
|
||||
@@ -484,30 +703,55 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
||||
(ratingData?.audienceRating && !!ratingData?.audienceScore)) && (
|
||||
<div className="media-ratings">
|
||||
{ratingData?.criticsRating && !!ratingData?.criticsScore && (
|
||||
<span className="media-rating">
|
||||
{ratingData.criticsRating === 'Rotten' ? (
|
||||
<RTRotten className="mr-1 w-6" />
|
||||
) : (
|
||||
<RTFresh className="mr-1 w-6" />
|
||||
)}
|
||||
{ratingData.criticsScore}%
|
||||
</span>
|
||||
<Tooltip
|
||||
content={intl.formatMessage(messages.rtcriticsscore)}
|
||||
>
|
||||
<a
|
||||
href={ratingData.url}
|
||||
className="media-rating"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{ratingData.criticsRating === 'Rotten' ? (
|
||||
<RTRotten className="mr-1 w-6" />
|
||||
) : (
|
||||
<RTFresh className="mr-1 w-6" />
|
||||
)}
|
||||
<span>{ratingData.criticsScore}%</span>
|
||||
</a>
|
||||
</Tooltip>
|
||||
)}
|
||||
{ratingData?.audienceRating && !!ratingData?.audienceScore && (
|
||||
<span className="media-rating">
|
||||
{ratingData.audienceRating === 'Spilled' ? (
|
||||
<RTAudRotten className="mr-1 w-6" />
|
||||
) : (
|
||||
<RTAudFresh className="mr-1 w-6" />
|
||||
)}
|
||||
{ratingData.audienceScore}%
|
||||
</span>
|
||||
<Tooltip
|
||||
content={intl.formatMessage(messages.rtaudiencescore)}
|
||||
>
|
||||
<a
|
||||
href={ratingData.url}
|
||||
className="media-rating"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{ratingData.audienceRating === 'Spilled' ? (
|
||||
<RTAudRotten className="mr-1 w-6" />
|
||||
) : (
|
||||
<RTAudFresh className="mr-1 w-6" />
|
||||
)}
|
||||
<span>{ratingData.audienceScore}%</span>
|
||||
</a>
|
||||
</Tooltip>
|
||||
)}
|
||||
{!!data.voteCount && (
|
||||
<span className="media-rating">
|
||||
<TmdbLogo className="mr-2 w-6" />
|
||||
{data.voteAverage}/10
|
||||
</span>
|
||||
<Tooltip content={intl.formatMessage(messages.tmdbuserscore)}>
|
||||
<a
|
||||
href={`https://www.themoviedb.org/tv/${data.id}?language=${locale}`}
|
||||
className="media-rating"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<TmdbLogo className="mr-1 w-6" />
|
||||
<span>{Math.round(data.voteAverage * 10)}%</span>
|
||||
</a>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user