Add Overseerr integration

This commit is contained in:
ajnart
2022-08-07 12:16:29 +02:00
parent b489c07177
commit 70814d0bc6
2 changed files with 202 additions and 4 deletions

View File

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

View File

@@ -0,0 +1,198 @@
import { Alert, Checkbox, createStyles, Group, LoadingOverlay, Stack, Table } from '@mantine/core';
import { openConfirmModal } from '@mantine/modals';
import { showNotification, updateNotification } from '@mantine/notifications';
import { IconAlertCircle, IconCheck, IconDownload } from '@tabler/icons';
import axios from 'axios';
import { useEffect, useState } from 'react';
import { useColorTheme } from '../../tools/color';
import { MovieResult } from './Movie.d';
import { MediaType, Result } from './SearchResult.d';
import { TvShowResult, TvShowResultSeason } from './TvShow.d';
interface RequestModalProps {
base: Result;
}
const useStyles = createStyles((theme) => ({
rowSelected: {
backgroundColor:
theme.colorScheme === 'dark'
? theme.fn.rgba(theme.colors[theme.primaryColor][7], 0.2)
: theme.colors[theme.primaryColor][0],
},
}));
export default function RequestModal({ base }: RequestModalProps) {
const [result, setResult] = useState<MovieResult | TvShowResult>();
useEffect(() => {
// Use the overseerr API get the media info.
axios.get(`/api/modules/overseerr/${base.id}?type=${base.mediaType}`).then((res) => {
setResult(res.data);
});
}, [base]);
const { secondaryColor } = useColorTheme();
if (!result) {
return <LoadingOverlay color={secondaryColor} visible />;
}
return base.mediaType === 'movie' ? (
<MovieRequestModal result={result as MovieResult} />
) : (
<TvRequestModal result={result as TvShowResult} />
);
}
function MovieRequestModal({ result }: { result: MovieResult }) {
const { secondaryColor } = useColorTheme();
openConfirmModal({
title: (
<Group>
<IconDownload />
Ask for {result.title}
</Group>
),
radius: 'lg',
labels: { confirm: 'Request', cancel: 'Cancel' },
onConfirm: () => {
askForMedia(MediaType.Movie, result.id, result.title);
},
size: 'lg',
children: (
<Stack>
<Alert
icon={<IconAlertCircle size={16} />}
title="Using API key"
color={secondaryColor}
radius="md"
variant="filled"
>
This request will be automatically approved
</Alert>
</Stack>
),
});
return null;
}
function TvRequestModal({ result }: { result: TvShowResult }) {
const [selection, setSelection] = useState<TvShowResultSeason[]>(result.seasons);
const { classes, cx } = useStyles();
const toggleRow = (container: TvShowResultSeason) =>
setSelection((current: TvShowResultSeason[]) =>
current.includes(container) ? current.filter((c) => c !== container) : [...current, container]
);
const toggleAll = () =>
setSelection((current: any) =>
current.length === result.seasons.length ? [] : result.seasons.map((c) => c)
);
const rows = result.seasons.map((element) => {
const selected = selection.includes(element);
return (
<tr key={element.id} className={cx({ [classes.rowSelected]: selected })}>
<td>
<Checkbox
key={element.id}
checked={selection.includes(element)}
onChange={() => toggleRow(element)}
transitionDuration={0}
/>
</td>
<td>{element.name}</td>
<td>{element.episodeCount}</td>
</tr>
);
});
const { secondaryColor } = useColorTheme();
openConfirmModal({
title: (
<Group>
<IconDownload />
Ask for {result.name}
</Group>
),
radius: 'lg',
labels: { confirm: 'Request', cancel: 'Cancel' },
confirmProps: {
disabled: selection.length === 0,
},
onConfirm: () => {
askForMedia(
MediaType.Tv,
result.id,
result.name,
selection.map((s) => s.seasonNumber)
);
},
size: 'lg',
children: (
<Stack>
<Alert
icon={<IconAlertCircle size={16} />}
title="Using API key"
color={secondaryColor}
radius="md"
variant="filled"
>
This request will be automatically approved
</Alert>
<Table captionSide="bottom" highlightOnHover>
<caption>Tick the seasons that you want to be downloaded</caption>
<thead>
<tr>
<th>
<Checkbox
onChange={toggleAll}
checked={selection.length === result.seasons.length}
indeterminate={selection.length > 0 && selection.length !== result.seasons.length}
transitionDuration={0}
/>
</th>
<th>Season</th>
<th>Number of episodes</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</Table>
</Stack>
),
});
return null;
}
function askForMedia(type: MediaType, id: number, name: string, seasons?: number[]) {
showNotification({
title: 'Request',
id: id.toString(),
message: `Requesting media ${name}`,
color: 'orange',
loading: true,
autoClose: false,
disallowClose: true,
icon: <IconAlertCircle />,
});
axios
.post(`/api/modules/overseerr/${id}`, { type, seasons })
.then(() => {
updateNotification({
id: id.toString(),
title: '',
color: 'green',
message: ` ${name} requested`,
icon: <IconCheck />,
autoClose: 2000,
});
})
.catch((err) => {
updateNotification({
id: id.toString(),
color: 'red',
title: 'There was an error',
message: err.message,
autoClose: 2000,
});
});
}