|
|
|
@ -1,6 +1,5 @@
|
|
|
|
|
import React, { useState, useContext, useMemo } from 'react';
|
|
|
|
|
import {
|
|
|
|
|
FormattedMessage,
|
|
|
|
|
defineMessages,
|
|
|
|
|
FormattedNumber,
|
|
|
|
|
FormattedDate,
|
|
|
|
@ -34,9 +33,9 @@ import RequestButton from '../RequestButton';
|
|
|
|
|
import MediaSlider from '../MediaSlider';
|
|
|
|
|
import ConfirmButton from '../Common/ConfirmButton';
|
|
|
|
|
import DownloadBlock from '../DownloadBlock';
|
|
|
|
|
import ButtonWithDropdown from '../Common/ButtonWithDropdown';
|
|
|
|
|
import PageTitle from '../Common/PageTitle';
|
|
|
|
|
import useSettings from '../../hooks/useSettings';
|
|
|
|
|
import PlayButton, { PlayButtonLink } from '../Common/PlayButton';
|
|
|
|
|
|
|
|
|
|
const messages = defineMessages({
|
|
|
|
|
releasedate: 'Release Date',
|
|
|
|
@ -110,11 +109,39 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|
|
|
|
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
|
|
|
|
|
?.filter((r) => r.type === 'Trailer')
|
|
|
|
|
.sort((a, b) => a.size - b.size)
|
|
|
|
|
.pop()?.url;
|
|
|
|
|
|
|
|
|
|
if (trailerUrl) {
|
|
|
|
|
mediaLinks.push({
|
|
|
|
|
text: intl.formatMessage(messages.watchtrailer),
|
|
|
|
|
url: trailerUrl,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const deleteMedia = async () => {
|
|
|
|
|
if (data?.mediaInfo?.id) {
|
|
|
|
|
await axios.delete(`/api/v1/media/${data?.mediaInfo?.id}`);
|
|
|
|
@ -395,95 +422,9 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|
|
|
|
</span>
|
|
|
|
|
</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">
|
|
|
|
|
{(trailerUrl ||
|
|
|
|
|
data.mediaInfo?.plexUrl ||
|
|
|
|
|
data.mediaInfo?.plexUrl4k) && (
|
|
|
|
|
<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">
|
|
|
|
|
<PlayButton links={mediaLinks} />
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mb-3 sm:mb-0">
|
|
|
|
|
<RequestButton
|
|
|
|
|
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-1 md:mr-8">
|
|
|
|
|
<h2 className="text-xl md:text-2xl">
|
|
|
|
|
<FormattedMessage {...messages.overview} />
|
|
|
|
|
{intl.formatMessage(messages.overview)}
|
|
|
|
|
</h2>
|
|
|
|
|
<p className="pt-2 text-sm md:text-base">
|
|
|
|
|
{data.overview
|
|
|
|
@ -595,11 +536,11 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
<div className="bg-gray-900 border border-gray-800 rounded-lg shadow">
|
|
|
|
|
{(data.voteCount ||
|
|
|
|
|
(ratingData?.criticsRating && ratingData?.criticsScore) ||
|
|
|
|
|
(ratingData?.audienceRating && ratingData?.audienceScore)) && (
|
|
|
|
|
{(!!data.voteCount ||
|
|
|
|
|
(ratingData?.criticsRating && !!ratingData?.criticsScore) ||
|
|
|
|
|
(ratingData?.audienceRating && !!ratingData?.audienceScore)) && (
|
|
|
|
|
<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">
|
|
|
|
|
{ratingData.criticsRating === 'Rotten' ? (
|
|
|
|
@ -613,7 +554,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|
|
|
|
</span>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
{ratingData?.audienceRating && ratingData?.audienceScore && (
|
|
|
|
|
{ratingData?.audienceRating && !!ratingData?.audienceScore && (
|
|
|
|
|
<>
|
|
|
|
|
<span className="text-sm">
|
|
|
|
|
{ratingData.audienceRating === 'Spilled' ? (
|
|
|
|
@ -627,7 +568,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|
|
|
|
</span>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
{data.voteCount > 0 && (
|
|
|
|
|
{!!data.voteCount && (
|
|
|
|
|
<>
|
|
|
|
|
<span className="text-sm">
|
|
|
|
|
<TmdbLogo className="w-6 mr-2" />
|
|
|
|
@ -642,7 +583,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|
|
|
|
{data.releaseDate && (
|
|
|
|
|
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
|
|
|
|
<span className="text-sm">
|
|
|
|
|
<FormattedMessage {...messages.releasedate} />
|
|
|
|
|
{intl.formatMessage(messages.releasedate)}
|
|
|
|
|
</span>
|
|
|
|
|
<span className="flex-1 text-sm text-right text-gray-400">
|
|
|
|
|
<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">
|
|
|
|
|
<span className="text-sm">
|
|
|
|
|
<FormattedMessage {...messages.status} />
|
|
|
|
|
{intl.formatMessage(messages.status)}
|
|
|
|
|
</span>
|
|
|
|
|
<span className="flex-1 text-sm text-right text-gray-400">
|
|
|
|
|
{data.status}
|
|
|
|
@ -665,7 +606,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|
|
|
|
{data.revenue > 0 && (
|
|
|
|
|
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
|
|
|
|
<span className="text-sm">
|
|
|
|
|
<FormattedMessage {...messages.revenue} />
|
|
|
|
|
{intl.formatMessage(messages.revenue)}
|
|
|
|
|
</span>
|
|
|
|
|
<span className="flex-1 text-sm text-right text-gray-400">
|
|
|
|
|
<FormattedNumber
|
|
|
|
@ -679,7 +620,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|
|
|
|
{data.budget > 0 && (
|
|
|
|
|
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
|
|
|
|
<span className="text-sm">
|
|
|
|
|
<FormattedMessage {...messages.budget} />
|
|
|
|
|
{intl.formatMessage(messages.budget)}
|
|
|
|
|
</span>
|
|
|
|
|
<span className="flex-1 text-sm text-right text-gray-400">
|
|
|
|
|
<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">
|
|
|
|
|
<span className="text-sm">
|
|
|
|
|
<FormattedMessage {...messages.originallanguage} />
|
|
|
|
|
{intl.formatMessage(messages.originallanguage)}
|
|
|
|
|
</span>
|
|
|
|
|
<span className="flex-1 text-sm text-right text-gray-400">
|
|
|
|
|
{
|
|
|
|
@ -709,7 +650,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|
|
|
|
{data.productionCompanies[0] && (
|
|
|
|
|
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
|
|
|
|
<span className="text-sm">
|
|
|
|
|
<FormattedMessage {...messages.studio} />
|
|
|
|
|
{intl.formatMessage(messages.studio)}
|
|
|
|
|
</span>
|
|
|
|
|
<span className="flex-1 text-sm text-right text-gray-400">
|
|
|
|
|
{data.productionCompanies[0]?.name}
|
|
|
|
@ -735,9 +676,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|
|
|
|
<div className="flex-1 min-w-0">
|
|
|
|
|
<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">
|
|
|
|
|
<span>
|
|
|
|
|
<FormattedMessage {...messages.cast} />
|
|
|
|
|
</span>
|
|
|
|
|
<span>{intl.formatMessage(messages.cast)}</span>
|
|
|
|
|
<svg
|
|
|
|
|
className="w-6 h-6 ml-2"
|
|
|
|
|
fill="none"
|
|
|
|
|