mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
refactor(ui): Create PlayButton component (#946)
This commit is contained in:
68
src/components/Common/PlayButton/index.tsx
Normal file
68
src/components/Common/PlayButton/index.tsx
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ButtonWithDropdown from '../ButtonWithDropdown';
|
||||||
|
|
||||||
|
interface PlayButtonProps {
|
||||||
|
links: PlayButtonLink[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PlayButtonLink {
|
||||||
|
text: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PlayButton: React.FC<PlayButtonProps> = ({ links }) => {
|
||||||
|
if (!links || !links.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ButtonWithDropdown
|
||||||
|
buttonType="ghost"
|
||||||
|
text={
|
||||||
|
<>
|
||||||
|
<svg
|
||||||
|
className="w-5 h-5 mr-1"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span>{links[0].text}</span>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
onClick={() => {
|
||||||
|
window.open(links[0].url, '_blank');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{links.length > 1 &&
|
||||||
|
links.slice(1).map((link, i) => {
|
||||||
|
return (
|
||||||
|
<ButtonWithDropdown.Item
|
||||||
|
key={`play-button-dropdown-item-${i}`}
|
||||||
|
onClick={() => {
|
||||||
|
window.open(link.url, '_blank');
|
||||||
|
}}
|
||||||
|
buttonType="ghost"
|
||||||
|
>
|
||||||
|
{link.text}
|
||||||
|
</ButtonWithDropdown.Item>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ButtonWithDropdown>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PlayButton;
|
@@ -1,6 +1,5 @@
|
|||||||
import React, { useState, useContext, useMemo } from 'react';
|
import React, { useState, useContext, useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
FormattedMessage,
|
|
||||||
defineMessages,
|
defineMessages,
|
||||||
FormattedNumber,
|
FormattedNumber,
|
||||||
FormattedDate,
|
FormattedDate,
|
||||||
@@ -34,9 +33,9 @@ import RequestButton from '../RequestButton';
|
|||||||
import MediaSlider from '../MediaSlider';
|
import MediaSlider from '../MediaSlider';
|
||||||
import ConfirmButton from '../Common/ConfirmButton';
|
import ConfirmButton from '../Common/ConfirmButton';
|
||||||
import DownloadBlock from '../DownloadBlock';
|
import DownloadBlock from '../DownloadBlock';
|
||||||
import ButtonWithDropdown from '../Common/ButtonWithDropdown';
|
|
||||||
import PageTitle from '../Common/PageTitle';
|
import PageTitle from '../Common/PageTitle';
|
||||||
import useSettings from '../../hooks/useSettings';
|
import useSettings from '../../hooks/useSettings';
|
||||||
|
import PlayButton, { PlayButtonLink } from '../Common/PlayButton';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
releasedate: 'Release Date',
|
releasedate: 'Release Date',
|
||||||
@@ -110,11 +109,39 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
return <Error statusCode={404} />;
|
return <Error statusCode={404} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mediaLinks: PlayButtonLink[] = [];
|
||||||
|
|
||||||
|
if (data.mediaInfo?.plexUrl) {
|
||||||
|
mediaLinks.push({
|
||||||
|
text: intl.formatMessage(messages.playonplex),
|
||||||
|
url: data.mediaInfo?.plexUrl,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
data.mediaInfo?.plexUrl4k &&
|
||||||
|
hasPermission([Permission.REQUEST_4K, Permission.REQUEST_4K_MOVIE], {
|
||||||
|
type: 'or',
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
mediaLinks.push({
|
||||||
|
text: intl.formatMessage(messages.play4konplex),
|
||||||
|
url: data.mediaInfo?.plexUrl4k,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const trailerUrl = data.relatedVideos
|
const trailerUrl = data.relatedVideos
|
||||||
?.filter((r) => r.type === 'Trailer')
|
?.filter((r) => r.type === 'Trailer')
|
||||||
.sort((a, b) => a.size - b.size)
|
.sort((a, b) => a.size - b.size)
|
||||||
.pop()?.url;
|
.pop()?.url;
|
||||||
|
|
||||||
|
if (trailerUrl) {
|
||||||
|
mediaLinks.push({
|
||||||
|
text: intl.formatMessage(messages.watchtrailer),
|
||||||
|
url: trailerUrl,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const deleteMedia = async () => {
|
const deleteMedia = async () => {
|
||||||
if (data?.mediaInfo?.id) {
|
if (data?.mediaInfo?.id) {
|
||||||
await axios.delete(`/api/v1/media/${data?.mediaInfo?.id}`);
|
await axios.delete(`/api/v1/media/${data?.mediaInfo?.id}`);
|
||||||
@@ -395,95 +422,9 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative z-10 flex flex-wrap justify-center flex-shrink-0 mt-4 sm:justify-end sm:flex-nowrap lg:mt-0">
|
<div className="relative z-10 flex flex-wrap justify-center flex-shrink-0 mt-4 sm:justify-end sm:flex-nowrap lg:mt-0">
|
||||||
{(trailerUrl ||
|
<div className="mb-3 sm:mb-0">
|
||||||
data.mediaInfo?.plexUrl ||
|
<PlayButton links={mediaLinks} />
|
||||||
data.mediaInfo?.plexUrl4k) && (
|
</div>
|
||||||
<div className="mb-3 sm:mb-0">
|
|
||||||
<ButtonWithDropdown
|
|
||||||
buttonType="ghost"
|
|
||||||
text={
|
|
||||||
<>
|
|
||||||
<svg
|
|
||||||
className="w-5 h-5 mr-1"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth={2}
|
|
||||||
d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth={2}
|
|
||||||
d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span>
|
|
||||||
{data.mediaInfo?.plexUrl
|
|
||||||
? intl.formatMessage(messages.playonplex)
|
|
||||||
: data.mediaInfo?.plexUrl4k &&
|
|
||||||
(hasPermission(Permission.REQUEST_4K) ||
|
|
||||||
hasPermission(Permission.REQUEST_4K_MOVIE))
|
|
||||||
? intl.formatMessage(messages.playonplex)
|
|
||||||
: intl.formatMessage(messages.watchtrailer)}
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
onClick={() => {
|
|
||||||
if (data.mediaInfo?.plexUrl) {
|
|
||||||
window.open(data.mediaInfo?.plexUrl, '_blank');
|
|
||||||
} else if (data.mediaInfo?.plexUrl4k) {
|
|
||||||
window.open(data.mediaInfo?.plexUrl4k, '_blank');
|
|
||||||
} else if (trailerUrl) {
|
|
||||||
window.open(trailerUrl, '_blank');
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{(
|
|
||||||
trailerUrl
|
|
||||||
? data.mediaInfo?.plexUrl ||
|
|
||||||
(data.mediaInfo?.plexUrl4k &&
|
|
||||||
(hasPermission(Permission.REQUEST_4K) ||
|
|
||||||
hasPermission(Permission.REQUEST_4K_MOVIE)))
|
|
||||||
: data.mediaInfo?.plexUrl &&
|
|
||||||
data.mediaInfo?.plexUrl4k &&
|
|
||||||
(hasPermission(Permission.REQUEST_4K) ||
|
|
||||||
hasPermission(Permission.REQUEST_4K_MOVIE))
|
|
||||||
) ? (
|
|
||||||
<>
|
|
||||||
{data.mediaInfo?.plexUrl &&
|
|
||||||
data.mediaInfo?.plexUrl4k &&
|
|
||||||
(hasPermission(Permission.REQUEST_4K) ||
|
|
||||||
hasPermission(Permission.REQUEST_4K_MOVIE)) && (
|
|
||||||
<ButtonWithDropdown.Item
|
|
||||||
onClick={() => {
|
|
||||||
window.open(data.mediaInfo?.plexUrl4k, '_blank');
|
|
||||||
}}
|
|
||||||
buttonType="ghost"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.play4konplex)}
|
|
||||||
</ButtonWithDropdown.Item>
|
|
||||||
)}
|
|
||||||
{trailerUrl && (
|
|
||||||
<ButtonWithDropdown.Item
|
|
||||||
onClick={() => {
|
|
||||||
window.open(trailerUrl, '_blank');
|
|
||||||
}}
|
|
||||||
buttonType="ghost"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.watchtrailer)}
|
|
||||||
</ButtonWithDropdown.Item>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
</ButtonWithDropdown>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="mb-3 sm:mb-0">
|
<div className="mb-3 sm:mb-0">
|
||||||
<RequestButton
|
<RequestButton
|
||||||
mediaType="movie"
|
mediaType="movie"
|
||||||
@@ -526,7 +467,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
<div className="flex flex-col pt-8 pb-4 text-white md:flex-row">
|
<div className="flex flex-col pt-8 pb-4 text-white md:flex-row">
|
||||||
<div className="flex-1 md:mr-8">
|
<div className="flex-1 md:mr-8">
|
||||||
<h2 className="text-xl md:text-2xl">
|
<h2 className="text-xl md:text-2xl">
|
||||||
<FormattedMessage {...messages.overview} />
|
{intl.formatMessage(messages.overview)}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="pt-2 text-sm md:text-base">
|
<p className="pt-2 text-sm md:text-base">
|
||||||
{data.overview
|
{data.overview
|
||||||
@@ -595,11 +536,11 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="bg-gray-900 border border-gray-800 rounded-lg shadow">
|
<div className="bg-gray-900 border border-gray-800 rounded-lg shadow">
|
||||||
{(data.voteCount ||
|
{(!!data.voteCount ||
|
||||||
(ratingData?.criticsRating && ratingData?.criticsScore) ||
|
(ratingData?.criticsRating && !!ratingData?.criticsScore) ||
|
||||||
(ratingData?.audienceRating && ratingData?.audienceScore)) && (
|
(ratingData?.audienceRating && !!ratingData?.audienceScore)) && (
|
||||||
<div className="flex items-center justify-center px-4 py-2 border-b border-gray-800 last:border-b-0">
|
<div className="flex items-center justify-center px-4 py-2 border-b border-gray-800 last:border-b-0">
|
||||||
{ratingData?.criticsRating && ratingData?.criticsScore && (
|
{ratingData?.criticsRating && !!ratingData?.criticsScore && (
|
||||||
<>
|
<>
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
{ratingData.criticsRating === 'Rotten' ? (
|
{ratingData.criticsRating === 'Rotten' ? (
|
||||||
@@ -613,7 +554,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{ratingData?.audienceRating && ratingData?.audienceScore && (
|
{ratingData?.audienceRating && !!ratingData?.audienceScore && (
|
||||||
<>
|
<>
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
{ratingData.audienceRating === 'Spilled' ? (
|
{ratingData.audienceRating === 'Spilled' ? (
|
||||||
@@ -627,7 +568,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{data.voteCount > 0 && (
|
{!!data.voteCount && (
|
||||||
<>
|
<>
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
<TmdbLogo className="w-6 mr-2" />
|
<TmdbLogo className="w-6 mr-2" />
|
||||||
@@ -642,7 +583,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
{data.releaseDate && (
|
{data.releaseDate && (
|
||||||
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
<FormattedMessage {...messages.releasedate} />
|
{intl.formatMessage(messages.releasedate)}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex-1 text-sm text-right text-gray-400">
|
<span className="flex-1 text-sm text-right text-gray-400">
|
||||||
<FormattedDate
|
<FormattedDate
|
||||||
@@ -656,7 +597,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
)}
|
)}
|
||||||
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
<FormattedMessage {...messages.status} />
|
{intl.formatMessage(messages.status)}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex-1 text-sm text-right text-gray-400">
|
<span className="flex-1 text-sm text-right text-gray-400">
|
||||||
{data.status}
|
{data.status}
|
||||||
@@ -665,7 +606,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
{data.revenue > 0 && (
|
{data.revenue > 0 && (
|
||||||
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
<FormattedMessage {...messages.revenue} />
|
{intl.formatMessage(messages.revenue)}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex-1 text-sm text-right text-gray-400">
|
<span className="flex-1 text-sm text-right text-gray-400">
|
||||||
<FormattedNumber
|
<FormattedNumber
|
||||||
@@ -679,7 +620,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
{data.budget > 0 && (
|
{data.budget > 0 && (
|
||||||
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
<FormattedMessage {...messages.budget} />
|
{intl.formatMessage(messages.budget)}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex-1 text-sm text-right text-gray-400">
|
<span className="flex-1 text-sm text-right text-gray-400">
|
||||||
<FormattedNumber
|
<FormattedNumber
|
||||||
@@ -695,7 +636,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
) && (
|
) && (
|
||||||
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
<FormattedMessage {...messages.originallanguage} />
|
{intl.formatMessage(messages.originallanguage)}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex-1 text-sm text-right text-gray-400">
|
<span className="flex-1 text-sm text-right text-gray-400">
|
||||||
{
|
{
|
||||||
@@ -709,7 +650,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
{data.productionCompanies[0] && (
|
{data.productionCompanies[0] && (
|
||||||
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
<FormattedMessage {...messages.studio} />
|
{intl.formatMessage(messages.studio)}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex-1 text-sm text-right text-gray-400">
|
<span className="flex-1 text-sm text-right text-gray-400">
|
||||||
{data.productionCompanies[0]?.name}
|
{data.productionCompanies[0]?.name}
|
||||||
@@ -735,9 +676,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<Link href="/movie/[movieId]/cast" as={`/movie/${data.id}/cast`}>
|
<Link href="/movie/[movieId]/cast" as={`/movie/${data.id}/cast`}>
|
||||||
<a className="inline-flex items-center text-xl leading-7 text-gray-300 hover:text-white sm:text-2xl sm:leading-9 sm:truncate">
|
<a className="inline-flex items-center text-xl leading-7 text-gray-300 hover:text-white sm:text-2xl sm:leading-9 sm:truncate">
|
||||||
<span>
|
<span>{intl.formatMessage(messages.cast)}</span>
|
||||||
<FormattedMessage {...messages.cast} />
|
|
||||||
</span>
|
|
||||||
<svg
|
<svg
|
||||||
className="w-6 h-6 ml-2"
|
className="w-6 h-6 ml-2"
|
||||||
fill="none"
|
fill="none"
|
||||||
|
@@ -1,10 +1,5 @@
|
|||||||
import React, { useState, useContext, useMemo } from 'react';
|
import React, { useState, useContext, useMemo } from 'react';
|
||||||
import {
|
import { FormattedDate, defineMessages, useIntl } from 'react-intl';
|
||||||
FormattedMessage,
|
|
||||||
FormattedDate,
|
|
||||||
defineMessages,
|
|
||||||
useIntl,
|
|
||||||
} from 'react-intl';
|
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import Button from '../Common/Button';
|
import Button from '../Common/Button';
|
||||||
@@ -36,9 +31,9 @@ import RequestButton from '../RequestButton';
|
|||||||
import MediaSlider from '../MediaSlider';
|
import MediaSlider from '../MediaSlider';
|
||||||
import ConfirmButton from '../Common/ConfirmButton';
|
import ConfirmButton from '../Common/ConfirmButton';
|
||||||
import DownloadBlock from '../DownloadBlock';
|
import DownloadBlock from '../DownloadBlock';
|
||||||
import ButtonWithDropdown from '../Common/ButtonWithDropdown';
|
|
||||||
import PageTitle from '../Common/PageTitle';
|
import PageTitle from '../Common/PageTitle';
|
||||||
import useSettings from '../../hooks/useSettings';
|
import useSettings from '../../hooks/useSettings';
|
||||||
|
import PlayButton, { PlayButtonLink } from '../Common/PlayButton';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
firstAirDate: 'First Air Date',
|
firstAirDate: 'First Air Date',
|
||||||
@@ -114,11 +109,39 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
return <Error statusCode={404} />;
|
return <Error statusCode={404} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mediaLinks: PlayButtonLink[] = [];
|
||||||
|
|
||||||
|
if (data.mediaInfo?.plexUrl) {
|
||||||
|
mediaLinks.push({
|
||||||
|
text: intl.formatMessage(messages.playonplex),
|
||||||
|
url: data.mediaInfo?.plexUrl,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
data.mediaInfo?.plexUrl4k &&
|
||||||
|
hasPermission([Permission.REQUEST_4K, Permission.REQUEST_4K_TV], {
|
||||||
|
type: 'or',
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
mediaLinks.push({
|
||||||
|
text: intl.formatMessage(messages.play4konplex),
|
||||||
|
url: data.mediaInfo?.plexUrl4k,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const trailerUrl = data.relatedVideos
|
const trailerUrl = data.relatedVideos
|
||||||
?.filter((r) => r.type === 'Trailer')
|
?.filter((r) => r.type === 'Trailer')
|
||||||
.sort((a, b) => a.size - b.size)
|
.sort((a, b) => a.size - b.size)
|
||||||
.pop()?.url;
|
.pop()?.url;
|
||||||
|
|
||||||
|
if (trailerUrl) {
|
||||||
|
mediaLinks.push({
|
||||||
|
text: intl.formatMessage(messages.watchtrailer),
|
||||||
|
url: trailerUrl,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const deleteMedia = async () => {
|
const deleteMedia = async () => {
|
||||||
if (data?.mediaInfo?.id) {
|
if (data?.mediaInfo?.id) {
|
||||||
await axios.delete(`/api/v1/media/${data?.mediaInfo?.id}`);
|
await axios.delete(`/api/v1/media/${data?.mediaInfo?.id}`);
|
||||||
@@ -423,95 +446,9 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative z-10 flex flex-wrap justify-center flex-shrink-0 mt-4 sm:justify-end sm:flex-nowrap lg:mt-0">
|
<div className="relative z-10 flex flex-wrap justify-center flex-shrink-0 mt-4 sm:justify-end sm:flex-nowrap lg:mt-0">
|
||||||
{(trailerUrl ||
|
<div className="mb-3 sm:mb-0">
|
||||||
data.mediaInfo?.plexUrl ||
|
<PlayButton links={mediaLinks} />
|
||||||
data.mediaInfo?.plexUrl4k) && (
|
</div>
|
||||||
<div className="mb-3 sm:mb-0">
|
|
||||||
<ButtonWithDropdown
|
|
||||||
buttonType="ghost"
|
|
||||||
text={
|
|
||||||
<>
|
|
||||||
<svg
|
|
||||||
className="w-5 h-5 mr-1"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth={2}
|
|
||||||
d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth={2}
|
|
||||||
d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span>
|
|
||||||
{data.mediaInfo?.plexUrl
|
|
||||||
? intl.formatMessage(messages.playonplex)
|
|
||||||
: data.mediaInfo?.plexUrl4k &&
|
|
||||||
(hasPermission(Permission.REQUEST_4K) ||
|
|
||||||
hasPermission(Permission.REQUEST_4K_TV))
|
|
||||||
? intl.formatMessage(messages.play4konplex)
|
|
||||||
: intl.formatMessage(messages.watchtrailer)}
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
onClick={() => {
|
|
||||||
if (data.mediaInfo?.plexUrl) {
|
|
||||||
window.open(data.mediaInfo?.plexUrl, '_blank');
|
|
||||||
} else if (data.mediaInfo?.plexUrl4k) {
|
|
||||||
window.open(data.mediaInfo?.plexUrl4k, '_blank');
|
|
||||||
} else if (trailerUrl) {
|
|
||||||
window.open(trailerUrl, '_blank');
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{(
|
|
||||||
trailerUrl
|
|
||||||
? data.mediaInfo?.plexUrl ||
|
|
||||||
(data.mediaInfo?.plexUrl4k &&
|
|
||||||
(hasPermission(Permission.REQUEST_4K) ||
|
|
||||||
hasPermission(Permission.REQUEST_4K_TV)))
|
|
||||||
: data.mediaInfo?.plexUrl &&
|
|
||||||
data.mediaInfo?.plexUrl4k &&
|
|
||||||
(hasPermission(Permission.REQUEST_4K) ||
|
|
||||||
hasPermission(Permission.REQUEST_4K_TV))
|
|
||||||
) ? (
|
|
||||||
<>
|
|
||||||
{data.mediaInfo?.plexUrl &&
|
|
||||||
data.mediaInfo?.plexUrl4k &&
|
|
||||||
(hasPermission(Permission.REQUEST_4K) ||
|
|
||||||
hasPermission(Permission.REQUEST_4K_TV)) ? (
|
|
||||||
<ButtonWithDropdown.Item
|
|
||||||
onClick={() => {
|
|
||||||
window.open(data.mediaInfo?.plexUrl4k, '_blank');
|
|
||||||
}}
|
|
||||||
buttonType="ghost"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.play4konplex)}
|
|
||||||
</ButtonWithDropdown.Item>
|
|
||||||
) : null}
|
|
||||||
{trailerUrl ? (
|
|
||||||
<ButtonWithDropdown.Item
|
|
||||||
onClick={() => {
|
|
||||||
window.open(trailerUrl, '_blank');
|
|
||||||
}}
|
|
||||||
buttonType="ghost"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.watchtrailer)}
|
|
||||||
</ButtonWithDropdown.Item>
|
|
||||||
) : null}
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
</ButtonWithDropdown>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="mb-3 sm:mb-0">
|
<div className="mb-3 sm:mb-0">
|
||||||
<RequestButton
|
<RequestButton
|
||||||
mediaType="tv"
|
mediaType="tv"
|
||||||
@@ -556,7 +493,7 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
<div className="flex flex-col pt-8 pb-4 text-white md:flex-row">
|
<div className="flex flex-col pt-8 pb-4 text-white md:flex-row">
|
||||||
<div className="flex-1 md:mr-8">
|
<div className="flex-1 md:mr-8">
|
||||||
<h2 className="text-xl md:text-2xl">
|
<h2 className="text-xl md:text-2xl">
|
||||||
<FormattedMessage {...messages.overview} />
|
{intl.formatMessage(messages.overview)}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="pt-2 text-sm md:text-base">
|
<p className="pt-2 text-sm md:text-base">
|
||||||
{data.overview
|
{data.overview
|
||||||
@@ -618,11 +555,11 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="w-full mt-8 md:w-80 md:mt-0">
|
<div className="w-full mt-8 md:w-80 md:mt-0">
|
||||||
<div className="bg-gray-900 border border-gray-800 rounded-lg shadow">
|
<div className="bg-gray-900 border border-gray-800 rounded-lg shadow">
|
||||||
{(data.voteCount ||
|
{(!!data.voteCount ||
|
||||||
(ratingData?.criticsRating && ratingData?.criticsScore) ||
|
(ratingData?.criticsRating && !!ratingData?.criticsScore) ||
|
||||||
(ratingData?.audienceRating && ratingData?.audienceScore)) && (
|
(ratingData?.audienceRating && !!ratingData?.audienceScore)) && (
|
||||||
<div className="flex items-center justify-center px-4 py-2 border-b border-gray-800 last:border-b-0">
|
<div className="flex items-center justify-center px-4 py-2 border-b border-gray-800 last:border-b-0">
|
||||||
{ratingData?.criticsRating && ratingData?.criticsScore && (
|
{ratingData?.criticsRating && !!ratingData?.criticsScore && (
|
||||||
<>
|
<>
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
{ratingData.criticsRating === 'Rotten' ? (
|
{ratingData.criticsRating === 'Rotten' ? (
|
||||||
@@ -636,7 +573,7 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{ratingData?.audienceRating && ratingData?.audienceScore && (
|
{ratingData?.audienceRating && !!ratingData?.audienceScore && (
|
||||||
<>
|
<>
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
{ratingData.audienceRating === 'Spilled' ? (
|
{ratingData.audienceRating === 'Spilled' ? (
|
||||||
@@ -650,7 +587,7 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{data.voteCount > 0 && (
|
{!!data.voteCount && (
|
||||||
<>
|
<>
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
<TmdbLogo className="w-6 mr-2" />
|
<TmdbLogo className="w-6 mr-2" />
|
||||||
@@ -677,7 +614,7 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
{data.firstAirDate && (
|
{data.firstAirDate && (
|
||||||
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
<FormattedMessage {...messages.firstAirDate} />
|
{intl.formatMessage(messages.firstAirDate)}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex-1 text-sm text-right text-gray-400">
|
<span className="flex-1 text-sm text-right text-gray-400">
|
||||||
<FormattedDate
|
<FormattedDate
|
||||||
@@ -692,7 +629,7 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
{data.nextEpisodeToAir && (
|
{data.nextEpisodeToAir && (
|
||||||
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
<FormattedMessage {...messages.nextAirDate} />
|
{intl.formatMessage(messages.nextAirDate)}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex-1 text-sm text-right text-gray-400">
|
<span className="flex-1 text-sm text-right text-gray-400">
|
||||||
<FormattedDate
|
<FormattedDate
|
||||||
@@ -706,7 +643,7 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
)}
|
)}
|
||||||
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
<FormattedMessage {...messages.status} />
|
{intl.formatMessage(messages.status)}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex-1 text-sm text-right text-gray-400">
|
<span className="flex-1 text-sm text-right text-gray-400">
|
||||||
{data.status}
|
{data.status}
|
||||||
@@ -717,7 +654,7 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
) && (
|
) && (
|
||||||
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
<FormattedMessage {...messages.originallanguage} />
|
{intl.formatMessage(messages.originallanguage)}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex-1 text-sm text-right text-gray-400">
|
<span className="flex-1 text-sm text-right text-gray-400">
|
||||||
{
|
{
|
||||||
@@ -731,7 +668,7 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
{data.networks.length > 0 && (
|
{data.networks.length > 0 && (
|
||||||
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
<FormattedMessage {...messages.network} />
|
{intl.formatMessage(messages.network)}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex-1 text-sm text-right text-gray-400">
|
<span className="flex-1 text-sm text-right text-gray-400">
|
||||||
{data.networks.map((n) => n.name).join(', ')}
|
{data.networks.map((n) => n.name).join(', ')}
|
||||||
@@ -757,9 +694,7 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<Link href="/tv/[tvId]/cast" as={`/tv/${data.id}/cast`}>
|
<Link href="/tv/[tvId]/cast" as={`/tv/${data.id}/cast`}>
|
||||||
<a className="inline-flex items-center text-xl leading-7 text-gray-300 hover:text-white sm:text-2xl sm:leading-9 sm:truncate">
|
<a className="inline-flex items-center text-xl leading-7 text-gray-300 hover:text-white sm:text-2xl sm:leading-9 sm:truncate">
|
||||||
<span>
|
<span>{intl.formatMessage(messages.cast)}</span>
|
||||||
<FormattedMessage {...messages.cast} />
|
|
||||||
</span>
|
|
||||||
<svg
|
<svg
|
||||||
className="w-6 h-6 ml-2"
|
className="w-6 h-6 ml-2"
|
||||||
fill="none"
|
fill="none"
|
||||||
|
Reference in New Issue
Block a user