import React, { useState, useContext, useMemo } from 'react'; import { FormattedMessage, defineMessages, FormattedNumber, FormattedDate, useIntl, } from 'react-intl'; import type { MovieDetails as MovieDetailsType } from '../../../server/models/Movie'; import useSWR from 'swr'; import { useRouter } from 'next/router'; import Button from '../Common/Button'; import Link from 'next/link'; import Slider from '../Slider'; import PersonCard from '../PersonCard'; import { LanguageContext } from '../../context/LanguageContext'; import LoadingSpinner from '../Common/LoadingSpinner'; import { useUser, Permission } from '../../hooks/useUser'; import { MediaStatus } from '../../../server/constants/media'; import axios from 'axios'; import SlideOver from '../Common/SlideOver'; import RequestBlock from '../RequestBlock'; import TmdbLogo from '../../assets/tmdb_logo.svg'; import RTFresh from '../../assets/rt_fresh.svg'; import RTRotten from '../../assets/rt_rotten.svg'; import RTAudFresh from '../../assets/rt_aud_fresh.svg'; import RTAudRotten from '../../assets/rt_aud_rotten.svg'; import type { RTRating } from '../../../server/api/rottentomatoes'; import Error from '../../pages/_error'; import ExternalLinkBlock from '../ExternalLinkBlock'; import { sortCrewPriority } from '../../utils/creditHelpers'; import StatusBadge from '../StatusBadge'; 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'; const messages = defineMessages({ releasedate: 'Release Date', userrating: 'User Rating', status: 'Status', revenue: 'Revenue', budget: 'Budget', watchtrailer: 'Watch Trailer', originallanguage: 'Original Language', overview: 'Overview', runtime: '{minutes} minutes', cast: 'Cast', recommendations: 'Recommendations', similar: 'Similar Titles', cancelrequest: 'Cancel Request', available: 'Available', unavailable: 'Unavailable', pending: 'Pending', overviewunavailable: 'Overview unavailable.', manageModalTitle: 'Manage Movie', manageModalRequests: 'Requests', manageModalNoRequests: 'No Requests', manageModalClearMedia: 'Clear All Media Data', manageModalClearMediaWarning: 'This will irreversibly remove all data for this movie, including any requests. If this item exists in your Plex library, the media information will be recreated during the next sync.', approve: 'Approve', decline: 'Decline', studio: 'Studio', viewfullcrew: 'View Full Crew', view: 'View', areyousure: 'Are you sure?', openradarr: 'Open Movie in Radarr', openradarr4k: 'Open Movie in 4K Radarr', downloadstatus: 'Download Status', playonplex: 'Play on Plex', play4konplex: 'Play 4K on Plex', markavailable: 'Mark as Available', mark4kavailable: 'Mark 4K as Available', }); interface MovieDetailsProps { movie?: MovieDetailsType; } const MovieDetails: React.FC = ({ movie }) => { const { hasPermission } = useUser(); const router = useRouter(); const intl = useIntl(); const { locale } = useContext(LanguageContext); const [showManager, setShowManager] = useState(false); const { data, error, revalidate } = useSWR( `/api/v1/movie/${router.query.movieId}?language=${locale}`, { initialData: movie, } ); const { data: ratingData } = useSWR( `/api/v1/movie/${router.query.movieId}/ratings` ); const sortedCrew = useMemo(() => sortCrewPriority(data?.credits.crew ?? []), [ data, ]); if (!data && !error) { return ; } if (!data) { return ; } const trailerUrl = data.relatedVideos ?.filter((r) => r.type === 'Trailer') .sort((a, b) => a.size - b.size) .pop()?.url; const deleteMedia = async () => { if (data?.mediaInfo?.id) { await axios.delete(`/api/v1/media/${data?.mediaInfo?.id}`); revalidate(); } }; const markAvailable = async (is4k = false) => { await axios.get(`/api/v1/media/${data?.mediaInfo?.id}/available`, { params: { is4k, }, }); revalidate(); }; return (
setShowManager(false)} subText={data.title} > {((data?.mediaInfo?.downloadStatus ?? []).length > 0 || (data?.mediaInfo?.downloadStatus4k ?? []).length > 0) && ( <>

{intl.formatMessage(messages.downloadstatus)}

    {data.mediaInfo?.downloadStatus?.map((status, index) => (
  • ))} {data.mediaInfo?.downloadStatus4k?.map((status, index) => (
  • ))}
)} {data?.mediaInfo && (data.mediaInfo.status !== MediaStatus.AVAILABLE || data.mediaInfo.status4k !== MediaStatus.AVAILABLE) && (
{data?.mediaInfo && data?.mediaInfo.status !== MediaStatus.AVAILABLE && (
)} {data?.mediaInfo && data?.mediaInfo.status4k !== MediaStatus.AVAILABLE && (
)}
)}

{intl.formatMessage(messages.manageModalRequests)}

    {data.mediaInfo?.requests?.map((request) => (
  • revalidate()} />
  • ))} {(data.mediaInfo?.requests ?? []).length === 0 && (
  • {intl.formatMessage(messages.manageModalNoRequests)}
  • )}
{(data?.mediaInfo?.serviceUrl || data?.mediaInfo?.serviceUrl4k) && (
{data?.mediaInfo?.serviceUrl && ( )} {data?.mediaInfo?.serviceUrl4k && ( )}
)} {data?.mediaInfo && (
deleteMedia()} confirmText={intl.formatMessage(messages.areyousure)} className="w-full" > {intl.formatMessage(messages.manageModalClearMedia)}
{intl.formatMessage(messages.manageModalClearMediaWarning)}
)}
{data.mediaInfo && data.mediaInfo.status !== MediaStatus.UNKNOWN && ( 0} plexUrl={data.mediaInfo?.plexUrl} plexUrl4k={data.mediaInfo?.plexUrl4k} /> )} 0} plexUrl={data.mediaInfo?.plexUrl} plexUrl4k={ data.mediaInfo?.plexUrl4k && (hasPermission(Permission.REQUEST_4K) || hasPermission(Permission.REQUEST_4K_MOVIE)) ? data.mediaInfo.plexUrl4k : undefined } />

{data.title}{' '} ({data.releaseDate.slice(0, 4)})

{(data.runtime ?? 0) > 0 && ( <> {' '} |{' '} )} {data.genres.map((g) => g.name).join(', ')}
{(trailerUrl || data.mediaInfo?.plexUrl || data.mediaInfo?.plexUrl4k) && ( {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)} } 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)) && ( { window.open(data.mediaInfo?.plexUrl4k, '_blank'); }} buttonType="ghost" > {intl.formatMessage(messages.play4konplex)} )} {trailerUrl && ( { window.open(trailerUrl, '_blank'); }} buttonType="ghost" > {intl.formatMessage(messages.watchtrailer)} )} ) : null} )}
revalidate()} />
{hasPermission(Permission.MANAGE_REQUESTS) && ( )}

{data.overview ? data.overview : intl.formatMessage(messages.overviewunavailable)}

{sortedCrew.length > 0 && ( )}
{data.collection && ( )}
{(data.voteCount > 0 || ratingData) && (
{ratingData?.criticsRating && (ratingData?.criticsScore ?? 0) > 0 && ( <> {ratingData.criticsRating === 'Rotten' ? ( ) : ( )} {ratingData.criticsScore}% )} {ratingData?.audienceRating && (ratingData?.audienceScore ?? 0) > 0 && ( <> {ratingData.audienceRating === 'Spilled' ? ( ) : ( )} {ratingData.audienceScore}% )} {data.voteCount > 0 && ( <> {data.voteAverage}/10 )}
)}
{data.status}
{data.revenue > 0 && (
)} {data.budget > 0 && (
)} {data.spokenLanguages.some( (lng) => lng.iso_639_1 === data.originalLanguage ) && (
{ data.spokenLanguages.find( (lng) => lng.iso_639_1 === data.originalLanguage )?.name }
)} {data.productionCompanies[0] && (
{data.productionCompanies[0]?.name}
)}
( ))} />
); }; export default MovieDetails;