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 type { MovieResult } from '../../../server/models/Search'; import Link from 'next/link'; import Slider from '../Slider'; import TitleCard from '../TitleCard'; 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 Head from 'next/head'; import ExternalLinkBlock from '../ExternalLinkBlock'; import { sortCrewPriority } from '../../utils/creditHelpers'; import StatusBadge from '../StatusBadge'; import RequestButton from './RequestButton'; 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 remove all media data including all requests for this item. This action is irreversible. If this item exists in your Plex library, the media information will be recreated next sync.', approve: 'Approve', decline: 'Decline', studio: 'Studio', viewfullcrew: 'View Full Crew', view: 'View', }); interface MovieDetailsProps { movie?: MovieDetailsType; } interface SearchResult { page: number; totalResults: number; totalPages: number; results: MovieResult[]; } 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: recommended, error: recommendedError } = useSWR( `/api/v1/movie/${router.query.movieId}/recommendations?language=${locale}` ); const { data: similar, error: similarError } = useSWR( `/api/v1/movie/${router.query.movieId}/similar?language=${locale}` ); 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(); } }; return (
{data.title} - Overseerr setShowManager(false)} subText={data.title} >

{intl.formatMessage(messages.manageModalRequests)}

    {data.mediaInfo?.requests?.map((request) => (
  • revalidate()} />
  • ))} {(data.mediaInfo?.requests ?? []).length === 0 && (
  • {intl.formatMessage(messages.manageModalNoRequests)}
  • )}
{data?.mediaInfo && (
{intl.formatMessage(messages.manageModalClearMediaWarning)}
)}
{data.mediaInfo && data.mediaInfo.status !== MediaStatus.UNKNOWN && ( )}

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

{(data.runtime ?? 0) > 0 && ( <> {' '} |{' '} )} {data.genres.map((g) => g.name).join(', ')}
{trailerUrl && ( )} 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}
)}
( ))} /> {(recommended?.results ?? []).length > 0 && ( <> ( ))} /> )} {(similar?.results ?? []).length > 0 && ( <> ( ))} /> )}
); }; export default MovieDetails;