Files
Homarr/src/modules/common/MediaDisplay.tsx

263 lines
8.2 KiB
TypeScript
Raw Normal View History

import { Badge, Button, Group, Image, Stack, Text, Title } from '@mantine/core';
2022-08-08 13:44:58 +02:00
import { IconDownload, IconExternalLink, IconPlayerPlay } from '@tabler/icons';
2022-08-22 09:50:54 +02:00
import { useTranslation } from 'next-i18next';
2022-08-08 13:44:58 +02:00
import { useState } from 'react';
import { useColorTheme } from '../../tools/color';
import { useConfig } from '../../tools/state';
import { serviceItem } from '../../tools/types';
2022-08-08 13:44:58 +02:00
import { RequestModal } from '../overseerr/RequestModal';
import { Result } from '../overseerr/SearchResult';
2022-04-30 21:36:53 +02:00
export interface IMedia {
overview: string;
imdbId?: any;
tmdbId?: any;
artist?: string;
2022-08-08 13:44:58 +02:00
title?: string;
type: 'movie' | 'tvshow' | 'book' | 'music' | 'overseer';
episodetitle?: string;
voteAverage?: string;
poster?: string;
2022-04-30 21:36:53 +02:00
genres: string[];
seasonNumber?: number;
plexUrl?: string;
episodeNumber?: number;
2022-08-07 12:16:15 +02:00
[key: string]: any;
2022-04-30 21:36:53 +02:00
}
2022-08-08 13:44:58 +02:00
export function OverseerrMediaDisplay(props: any) {
const { media }: { media: Result } = props;
const { config } = useConfig();
const service = config.apps.find(
(service) => service.type === 'Overseerr' || service.type === 'Jellyseerr'
);
2022-04-30 21:36:53 +02:00
return (
2022-08-08 13:44:58 +02:00
<MediaDisplay
media={{
...media,
genres: [],
overview: media.overview ?? '',
title: media.title ?? media.name ?? media.originalName,
2022-08-08 13:44:58 +02:00
poster: `https://image.tmdb.org/t/p/w600_and_h900_bestv2/${media.posterPath}`,
seasonNumber: media.mediaInfo?.seasons.length,
episodetitle: media.title,
plexUrl: media.mediaInfo?.plexUrl ?? media.mediaInfo?.mediaUrl,
voteAverage: media.voteAverage?.toString(),
2022-08-08 13:44:58 +02:00
overseerrResult: media,
overseerrId: `${service?.openedUrl ? service?.openedUrl : service?.url}/${
media.mediaType
}/${media.id}`,
2022-08-08 13:44:58 +02:00
type: 'overseer',
}}
/>
);
}
export function ReadarrMediaDisplay(props: any) {
const { media }: { media: any } = props;
const { config } = useConfig();
// Find lidarr in services
const readarr = config.apps.find((service: serviceItem) => service.type === 'Readarr');
// Find a poster CoverType
const poster = media.images.find((image: any) => image.coverType === 'cover');
if (!readarr) {
return null;
}
const baseUrl = readarr.openedUrl
? new URL(readarr.openedUrl).origin
: new URL(readarr.url).origin;
// Remove '/' from the end of the lidarr url
const fullLink = poster ? `${baseUrl}${poster.url}` : undefined;
// Return a movie poster containting the title and the description
return (
<MediaDisplay
media={{
2022-08-08 13:44:58 +02:00
...media,
title: media.title,
poster: fullLink,
2022-08-08 13:44:58 +02:00
artist: media.authorTitle,
overview: `new book release by ${media.authorTitle}`,
genres: media.genres ?? [],
voteAverage: media.ratings.value.toString(),
2022-08-08 13:44:58 +02:00
type: 'book',
}}
/>
);
}
export function LidarrMediaDisplay(props: any) {
const { media }: { media: any } = props;
const { config } = useConfig();
// Find lidarr in services
const lidarr = config.apps.find((service: serviceItem) => service.type === 'Lidarr');
// Find a poster CoverType
const poster = media.images.find((image: any) => image.coverType === 'cover');
if (!lidarr) {
return null;
}
const baseUrl = lidarr.openedUrl ? new URL(lidarr.openedUrl).origin : new URL(lidarr.url).origin;
// Remove '/' from the end of the lidarr url
const fullLink = poster ? `${baseUrl}${poster.url}` : undefined;
// Return a movie poster containting the title and the description
return (
<MediaDisplay
media={{
2022-08-08 13:44:58 +02:00
type: 'music',
title: media.title,
poster: fullLink,
artist: media.artist.artistName,
overview: media.overview,
genres: media.genres,
}}
/>
);
}
export function RadarrMediaDisplay(props: any) {
const { media }: { media: any } = props;
// Find a poster CoverType
return (
<MediaDisplay
media={{
2022-08-08 13:44:58 +02:00
...media,
title: media.title ?? media.originalTitle,
overview: media.overview ?? '',
genres: media.genres ?? [],
poster: media.images.find((image: any) => image.coverType === 'poster')?.url,
voteAverage: media.ratings.tmdb.value.toString(),
imdbId: media.imdbId,
2022-08-08 13:44:58 +02:00
type: 'movie',
}}
/>
);
}
export function SonarrMediaDisplay(props: any) {
const { media }: { media: any } = props;
// Find a poster CoverType
const poster = media.series.images.find((image: any) => image.coverType === 'poster');
// Return a movie poster containting the title and the description
return (
<MediaDisplay
media={{
2022-08-08 13:44:58 +02:00
...media,
genres: media.series.genres ?? [],
overview: media.overview ?? media.series.overview ?? '',
title: media.series.title,
2022-08-08 13:44:58 +02:00
poster: poster ? poster.url : undefined,
episodeNumber: media.episodeNumber,
seasonNumber: media.seasonNumber,
episodetitle: media.title,
imdbId: media.series.imdbId,
voteAverage: media.series.ratings.value.toString(),
2022-08-08 13:44:58 +02:00
type: 'tvshow',
}}
/>
2022-04-30 21:36:53 +02:00
);
}
2022-08-08 13:44:58 +02:00
export function MediaDisplay({ media }: { media: IMedia }) {
const [opened, setOpened] = useState(false);
const { secondaryColor } = useColorTheme();
const { t } = useTranslation('modules/common-media-cards');
2022-08-08 13:44:58 +02:00
return (
2022-10-16 23:40:47 +09:00
<Group mr="xs" align="stretch" noWrap style={{ maxHeight: 200 }} spacing="xs">
2022-08-08 13:44:58 +02:00
<Image src={media.poster} height={200} width={150} radius="md" fit="cover" />
<Stack justify="space-around">
<Stack spacing="sm">
<Text lineClamp={2}>
<Title order={5}>{media.title}</Title>
</Text>
<Group spacing="xs">
{media.type === 'tvshow' && (
<Badge variant="dot" size="xs" radius="md" color="blue">
s{media.seasonNumber}e{media.episodeNumber} - {media.episodetitle}
</Badge>
)}
{media.type === 'music' && (
<Badge variant="dot" size="xs" radius="md" color="green">
{media.artist}
</Badge>
)}
{media.type === 'movie' && (
<Badge variant="dot" size="xs" radius="md" color="orange">
Radarr
</Badge>
)}
{media.type === 'book' && (
<Badge variant="dot" size="xs" radius="md" color="red">
Readarr
</Badge>
)}
{media.genres.slice(0, 2).map((genre) => (
<Badge size="xs" radius="md" key={genre}>
{genre}
</Badge>
))}
</Group>
<Text color="dimmed" size="xs" lineClamp={4}>
{media.overview}
</Text>
</Stack>
2022-08-10 14:01:23 +02:00
<Group noWrap>
2022-08-09 15:04:39 +02:00
{media.plexUrl && (
2022-08-08 13:44:58 +02:00
<Button
component="a"
target="_blank"
variant="outline"
2022-08-09 15:04:39 +02:00
href={media.plexUrl}
2022-08-08 13:44:58 +02:00
size="sm"
rightIcon={<IconPlayerPlay size={15} />}
>
2022-08-22 09:50:54 +02:00
{t('buttons.play')}
2022-08-08 13:44:58 +02:00
</Button>
)}
{media.imdbId && (
2022-08-10 14:13:20 +02:00
<Button
2022-08-08 13:44:58 +02:00
component="a"
target="_blank"
href={`https://www.imdb.com/title/${media.imdbId}`}
variant="outline"
2022-08-10 14:13:20 +02:00
size="sm"
rightIcon={<IconExternalLink size={15} />}
2022-08-08 13:44:58 +02:00
>
2022-08-10 14:13:20 +02:00
IMDb
</Button>
)}
{media.overseerrId && (
2022-08-10 14:13:20 +02:00
<Button
component="a"
target="_blank"
href={media.overseerrId}
variant="outline"
2022-08-10 14:13:20 +02:00
size="sm"
rightIcon={<IconExternalLink size={15} />}
>
2022-08-10 14:13:20 +02:00
TMDb
</Button>
2022-08-08 13:44:58 +02:00
)}
{media.type === 'overseer' && !media.overseerrResult?.mediaInfo?.mediaAddedAt && (
2022-08-08 13:44:58 +02:00
<>
<RequestModal
base={media.overseerrResult as Result}
opened={opened}
setOpened={setOpened}
/>
<Button
onClick={() => setOpened(true)}
color={secondaryColor}
2022-08-08 13:44:58 +02:00
size="sm"
rightIcon={<IconDownload size={15} />}
>
2022-08-22 09:50:54 +02:00
{t('buttons.request')}
2022-08-08 13:44:58 +02:00
</Button>
</>
)}
</Group>
</Stack>
</Group>
);
}