Improve MediaDisplay overseerr

This commit is contained in:
ajnart
2022-08-08 13:44:58 +02:00
parent 04874e69f2
commit 9a53f5d1ee
3 changed files with 140 additions and 129 deletions

View File

@@ -1,26 +1,18 @@
import { import { Badge, Button, Group, Image, Stack, Text, Title } from '@mantine/core';
Image, import { IconDownload, IconExternalLink, IconPlayerPlay } from '@tabler/icons';
Group, import { useState } from 'react';
Title,
Badge,
Text,
ActionIcon,
Anchor,
ScrollArea,
createStyles,
Stack,
Button,
} from '@mantine/core';
import { useMediaQuery } from '@mantine/hooks';
import { IconLink } from '@tabler/icons';
import { useConfig } from '../../tools/state'; import { useConfig } from '../../tools/state';
import { serviceItem } from '../../tools/types'; import { serviceItem } from '../../tools/types';
import { RequestModal } from '../overseerr/RequestModal';
import { Result } from '../overseerr/SearchResult';
export interface IMedia { export interface IMedia {
overview: string; overview: string;
imdbId?: any; imdbId?: any;
artist?: string; artist?: string;
title: string; title?: string;
type: 'movie' | 'tvshow' | 'book' | 'music' | 'overseer';
episodetitle?: string;
voteAverage?: string; voteAverage?: string;
poster?: string; poster?: string;
genres: string[]; genres: string[];
@@ -30,83 +22,24 @@ export interface IMedia {
[key: string]: any; [key: string]: any;
} }
const useStyles = createStyles((theme) => ({ export function OverseerrMediaDisplay(props: any) {
overview: { const { media }: { media: Result } = props;
[theme.fn.largerThan('sm')]: {
width: 400,
},
},
}));
export function MediaDisplay(props: { media: IMedia }) {
const { media }: { media: IMedia } = props;
const { classes, cx } = useStyles();
const phone = useMediaQuery('(min-width: 800px)');
return ( return (
<Group {...props} position="apart"> <MediaDisplay
<Text> media={{
{media.poster && ( ...media,
<Image genres: [],
width={phone ? 250 : 100} overview: media.overview ?? '',
height={phone ? 400 : 160} title: media.title ?? media.name ?? media.originalName ?? undefined,
style={{ poster: `https://image.tmdb.org/t/p/w600_and_h900_bestv2/${media.posterPath}`,
float: 'right', seasonNumber: media.mediaInfo?.seasons.length ?? undefined,
episodetitle: media.title ?? undefined,
plexUrl: media.mediaInfo?.plexUrl ?? undefined,
voteAverage: media.voteAverage?.toString() ?? undefined,
overseerrResult: media,
type: 'overseer',
}} }}
radius="md"
fit="cover"
src={media.poster}
alt={media.title}
/> />
)}
<Stack style={{ minWidth: phone ? 450 : '65vw' }}>
<Group noWrap mr="sm" className={classes.overview}>
<Title order={3}>{media.title}</Title>
{media.artist && <Text color="gray">New release from {media.artist}</Text>}
{(media.episodeNumber || media.seasonNumber) && (
<Text color="gray">
Season {media.seasonNumber}{' '}
{media.episodeNumber && `episode ${media.episodeNumber}`}
</Text>
)}
</Group>
{media.voteAverage && (
<Button
radius="md"
variant="light"
color={media.plexUrl ? 'green' : 'cyan'}
size="md"
onClick={
media.plexUrl
? () => window.open(media.plexUrl)
: () => {
// TODO: implement overseerr media requests
console.log(media);
}
}
>
{media.plexUrl ? 'Available on Plex' : 'Request'}
</Button>
)}
{media.imdbId && (
<Anchor href={`https://www.imdb.com/title/${media.imdbId}`} target="_blank">
<ActionIcon>
<IconLink />
</ActionIcon>
</Anchor>
)}
</Stack>
<Stack>
<ScrollArea style={{ height: 280, maxWidth: 700 }}>{media.overview}</ScrollArea>
<Group align="center" position="center" spacing="xs">
{media.genres.slice(-5).map((genre: string, i: number) => (
<Badge size="sm" key={i}>
{genre}
</Badge>
))}
</Group>
</Stack>
</Text>
</Group>
); );
} }
@@ -127,11 +60,14 @@ export function ReadarrMediaDisplay(props: any) {
return ( return (
<MediaDisplay <MediaDisplay
media={{ media={{
...media,
title: media.title, title: media.title,
poster: fullLink, poster: fullLink,
artist: media.author.authorName, artist: media.authorTitle,
overview: media.overview, overview: `new book release by ${media.authorTitle}`,
genres: media.genres, genres: media.genres ?? [],
voteAverage: media.ratings.value.toString() ?? undefined,
type: 'book',
}} }}
/> />
); );
@@ -154,6 +90,7 @@ export function LidarrMediaDisplay(props: any) {
return ( return (
<MediaDisplay <MediaDisplay
media={{ media={{
type: 'music',
title: media.title, title: media.title,
poster: fullLink, poster: fullLink,
artist: media.artist.artistName, artist: media.artist.artistName,
@@ -167,16 +104,17 @@ export function LidarrMediaDisplay(props: any) {
export function RadarrMediaDisplay(props: any) { export function RadarrMediaDisplay(props: any) {
const { media }: { media: any } = props; const { media }: { media: any } = props;
// Find a poster CoverType // Find a poster CoverType
const poster = media.images.find((image: any) => image.coverType === 'poster');
// Return a movie poster containting the title and the description
return ( return (
<MediaDisplay <MediaDisplay
media={{ media={{
imdbId: media.imdbId, ...media,
title: media.title, title: media.title ?? media.originalTitle,
overview: media.overview, overview: media.overview ?? '',
poster: poster.url, genres: media.genres ?? [],
genres: media.genres, poster: media.images.find((image: any) => image.coverType === 'poster')?.url ?? undefined,
voteAverage: media.ratings.tmdb.value.toString() ?? undefined,
imdbId: media.imdbId ?? undefined,
type: 'movie',
}} }}
/> />
); );
@@ -190,14 +128,106 @@ export function SonarrMediaDisplay(props: any) {
return ( return (
<MediaDisplay <MediaDisplay
media={{ media={{
imdbId: media.series.imdbId, ...media,
title: media.series.title, genres: media.series.genres ?? [],
overview: media.series.overview, overview: media.overview ?? media.series.overview ?? '',
poster: poster.url, title: media.series.title ?? undefined,
genres: media.series.genres, poster: poster ? poster.url : undefined,
seasonNumber: media.seasonNumber, episodeNumber: media.episodeNumber ?? undefined,
episodeNumber: media.episodeNumber, seasonNumber: media.seasonNumber ?? undefined,
episodetitle: media.title ?? undefined,
imdbId: media.series.imdbId ?? undefined,
voteAverage: media.series.ratings.value.toString() ?? undefined,
type: 'tvshow',
}} }}
/> />
); );
} }
export function MediaDisplay({ media }: { media: IMedia }) {
const [opened, setOpened] = useState(false);
return (
<Group mr="xs" align="stretch" noWrap style={{ maxHeight: 200 }}>
<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>
<Group grow>
{media.plexUrl && (
<Button
component="a"
target="_blank"
variant="outline"
href={media.plexUrl}
size="sm"
rightIcon={<IconPlayerPlay size={15} />}
>
Play
</Button>
)}
{media.imdbId && (
<Button
component="a"
target="_blank"
href={`https://www.imdb.com/title/${media.imdbId}`}
variant="outline"
size="sm"
rightIcon={<IconExternalLink size={15} />}
>
IMDb
</Button>
)}
{media.type === 'overseer' && (
<>
<RequestModal
base={media.overseerrResult as Result}
opened={opened}
setOpened={setOpened}
/>
<Button
onClick={() => setOpened(true)}
size="sm"
rightIcon={<IconDownload size={15} />}
>
Request
</Button>
</>
)}
</Group>
</Stack>
</Group>
);
}

View File

@@ -1,19 +0,0 @@
import { MediaDisplay } from '../common';
import { Result } from './SearchResult';
export default function OverseerrMediaDisplay(props: any) {
const { media }: { media: Result } = props;
return (
<MediaDisplay
media={{
title: media.name ?? media.originalTitle ?? media.title ?? '',
poster: `https://image.tmdb.org/t/p/w600_and_h900_bestv2/${media.posterPath}`,
genres: [`score: ${media.voteAverage}/10`],
seasonNumber: media.mediaInfo?.seasons.length,
plexUrl: media.mediaInfo?.plexUrl,
imdbId: media.mediaInfo?.imdbId,
overview: media.overview,
}}
/>
);
}

View File

@@ -1,6 +1,6 @@
import { IconEyeglass } from '@tabler/icons'; import { IconEyeglass } from '@tabler/icons';
import { OverseerrMediaDisplay } from '../common';
import { IModule } from '../ModuleTypes'; import { IModule } from '../ModuleTypes';
import OverseerrMediaDisplay from './OverseerrMediaDisplay';
export const OverseerrModule: IModule = { export const OverseerrModule: IModule = {
title: 'Overseerr', title: 'Overseerr',