mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-10 07:25:48 +01:00
✨ Implement new movie search modals
This commit is contained in:
@@ -20,83 +20,9 @@ interface IntegrationSelectorProps {
|
||||
export const IntegrationSelector = ({ form }: IntegrationSelectorProps) => {
|
||||
const { t } = useTranslation('layout/modals/add-app');
|
||||
|
||||
const data: SelectItem[] = [
|
||||
{
|
||||
value: 'sabnzbd',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/sabnzbd.png',
|
||||
label: 'SABnzbd',
|
||||
},
|
||||
{
|
||||
value: 'nzbGet',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/nzbget.png',
|
||||
label: 'NZBGet',
|
||||
},
|
||||
{
|
||||
value: 'deluge',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/deluge.png',
|
||||
label: 'Deluge',
|
||||
},
|
||||
{
|
||||
value: 'transmission',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/transmission.png',
|
||||
label: 'Transmission',
|
||||
},
|
||||
{
|
||||
value: 'qBittorrent',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/qbittorrent.png',
|
||||
label: 'qBittorrent',
|
||||
},
|
||||
{
|
||||
value: 'jellyseerr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/jellyseerr.png',
|
||||
label: 'Jellyseerr',
|
||||
},
|
||||
{
|
||||
value: 'overseerr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/overseerr.png',
|
||||
label: 'Overseerr',
|
||||
},
|
||||
{
|
||||
value: 'sonarr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/sonarr.png',
|
||||
label: 'Sonarr',
|
||||
},
|
||||
{
|
||||
value: 'radarr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/radarr.png',
|
||||
label: 'Radarr',
|
||||
},
|
||||
{
|
||||
value: 'lidarr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/lidarr.png',
|
||||
label: 'Lidarr',
|
||||
},
|
||||
{
|
||||
value: 'readarr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/readarr.png',
|
||||
label: 'Readarr',
|
||||
},
|
||||
{
|
||||
value: 'jellyfin',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/jellyfin.png',
|
||||
label: 'Jellyfin',
|
||||
},
|
||||
{
|
||||
value: 'plex',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/plex.png',
|
||||
label: 'Plex',
|
||||
},
|
||||
{
|
||||
value: 'pihole',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/pi-hole.png',
|
||||
label: 'PiHole',
|
||||
},
|
||||
{
|
||||
value: 'adGuardHome',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/adguard-home.png',
|
||||
label: 'AdGuard Home',
|
||||
},
|
||||
].filter((x) => Object.keys(integrationFieldProperties).includes(x.value));
|
||||
const data = availableIntegrations.filter((x) =>
|
||||
Object.keys(integrationFieldProperties).includes(x.value)
|
||||
);
|
||||
|
||||
const getNewProperties = (value: string | null): AppIntegrationPropertyType[] => {
|
||||
if (!value) return [];
|
||||
@@ -181,3 +107,81 @@ const SelectItemComponent = forwardRef<HTMLDivElement, ItemProps>(
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
export const availableIntegrations = [
|
||||
{
|
||||
value: 'sabnzbd',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/sabnzbd.png',
|
||||
label: 'SABnzbd',
|
||||
},
|
||||
{
|
||||
value: 'nzbGet',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/nzbget.png',
|
||||
label: 'NZBGet',
|
||||
},
|
||||
{
|
||||
value: 'deluge',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/deluge.png',
|
||||
label: 'Deluge',
|
||||
},
|
||||
{
|
||||
value: 'transmission',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/transmission.png',
|
||||
label: 'Transmission',
|
||||
},
|
||||
{
|
||||
value: 'qBittorrent',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/qbittorrent.png',
|
||||
label: 'qBittorrent',
|
||||
},
|
||||
{
|
||||
value: 'jellyseerr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/jellyseerr.png',
|
||||
label: 'Jellyseerr',
|
||||
},
|
||||
{
|
||||
value: 'overseerr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/overseerr.png',
|
||||
label: 'Overseerr',
|
||||
},
|
||||
{
|
||||
value: 'sonarr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/sonarr.png',
|
||||
label: 'Sonarr',
|
||||
},
|
||||
{
|
||||
value: 'radarr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/radarr.png',
|
||||
label: 'Radarr',
|
||||
},
|
||||
{
|
||||
value: 'lidarr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/lidarr.png',
|
||||
label: 'Lidarr',
|
||||
},
|
||||
{
|
||||
value: 'readarr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/readarr.png',
|
||||
label: 'Readarr',
|
||||
},
|
||||
{
|
||||
value: 'jellyfin',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/jellyfin.png',
|
||||
label: 'Jellyfin',
|
||||
},
|
||||
{
|
||||
value: 'plex',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/plex.png',
|
||||
label: 'Plex',
|
||||
},
|
||||
{
|
||||
value: 'pihole',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/pi-hole.png',
|
||||
label: 'PiHole',
|
||||
},
|
||||
{
|
||||
value: 'adGuardHome',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/adguard-home.png',
|
||||
label: 'AdGuard Home',
|
||||
},
|
||||
] as const satisfies Readonly<SelectItem[]>;
|
||||
|
||||
@@ -301,12 +301,13 @@ const getOpenTarget = (config: ConfigType | undefined): '_blank' | '_self' => {
|
||||
return config.settings.common.searchEngine.properties.openInNewTab ? '_blank' : '_self';
|
||||
};
|
||||
|
||||
const useOverseerrSearchQuery = (query: string, isEnabled: boolean) => {
|
||||
export const useOverseerrSearchQuery = (query: string, isEnabled: boolean) => {
|
||||
const { name: configName } = useConfigContext();
|
||||
return api.overseerr.all.useQuery(
|
||||
return api.overseerr.search.useQuery(
|
||||
{
|
||||
query,
|
||||
configName: configName!,
|
||||
integration: 'overseerr',
|
||||
},
|
||||
{
|
||||
enabled: isEnabled,
|
||||
|
||||
204
src/components/layout/new-header/MovieModal.tsx
Normal file
204
src/components/layout/new-header/MovieModal.tsx
Normal file
@@ -0,0 +1,204 @@
|
||||
import {
|
||||
Button,
|
||||
Center,
|
||||
Divider,
|
||||
Group,
|
||||
Loader,
|
||||
Image as MantineImage,
|
||||
Modal,
|
||||
ScrollArea,
|
||||
Stack,
|
||||
Text,
|
||||
Title,
|
||||
} from '@mantine/core';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import { IconDownload, IconExternalLink, IconPlayerPlay } from '@tabler/icons-react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import Image from 'next/image';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useMemo } from 'react';
|
||||
import { z } from 'zod';
|
||||
import { availableIntegrations } from '~/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/IntegrationSelector';
|
||||
import { useConfigContext } from '~/config/provider';
|
||||
import { RequestModal } from '~/modules/overseerr/RequestModal';
|
||||
import { RouterOutputs, api } from '~/utils/api';
|
||||
|
||||
type MovieModalProps = {
|
||||
opened: boolean;
|
||||
closeModal: () => void;
|
||||
};
|
||||
|
||||
const queryParamsSchema = z.object({
|
||||
movie: z.literal('true'),
|
||||
search: z.string().nonempty(),
|
||||
type: z.enum(['jellyseerr', 'overseerr']),
|
||||
});
|
||||
|
||||
type MovieResultsProps = z.infer<typeof queryParamsSchema> & {
|
||||
closeModal: () => void;
|
||||
};
|
||||
|
||||
const MovieResults = ({ search, type, closeModal }: MovieResultsProps) => {
|
||||
const { name: configName } = useConfigContext();
|
||||
const { data: overseerrResults, isLoading } = api.overseerr.search.useQuery(
|
||||
{
|
||||
query: search,
|
||||
configName: configName!,
|
||||
integration: type,
|
||||
},
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
refetchInterval: false,
|
||||
}
|
||||
);
|
||||
|
||||
if (isLoading)
|
||||
return (
|
||||
<Center>
|
||||
<Loader />
|
||||
</Center>
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Text>
|
||||
Found {overseerrResults?.length} results for <b>{search}</b>
|
||||
</Text>
|
||||
<Stack spacing="xs">
|
||||
{overseerrResults?.map((result, index: number) => (
|
||||
<React.Fragment key={index}>
|
||||
<MovieDisplay movie={result} type={type} closeModal={closeModal} />
|
||||
{index < overseerrResults.length - 1 && <Divider variant="dashed" my="xs" />}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export const MovieModal = ({ opened, closeModal }: MovieModalProps) => {
|
||||
const query = useRouter().query;
|
||||
const queryParams = queryParamsSchema.safeParse(query);
|
||||
|
||||
if (!queryParams.success) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const integration = useMemo(() => {
|
||||
return availableIntegrations.find((x) => x.value === queryParams.data.type)!;
|
||||
}, [queryParams.data.type]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
opened={opened}
|
||||
onClose={closeModal}
|
||||
size="lg"
|
||||
scrollAreaComponent={ScrollArea.Autosize}
|
||||
title={
|
||||
<Group>
|
||||
<Image src={integration.image} width={30} height={30} alt={`${integration.label} icon`} />
|
||||
<Title order={4}>{integration.label} movies</Title>
|
||||
</Group>
|
||||
}
|
||||
>
|
||||
<MovieResults
|
||||
movie={queryParams.data.movie}
|
||||
search={queryParams.data.search}
|
||||
type={queryParams.data.type}
|
||||
closeModal={closeModal}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
type MovieDisplayProps = {
|
||||
movie: RouterOutputs['overseerr']['search'][number];
|
||||
type: 'jellyseerr' | 'overseerr';
|
||||
closeModal: () => void;
|
||||
};
|
||||
|
||||
const MovieDisplay = ({ movie, type, closeModal }: MovieDisplayProps) => {
|
||||
const { t } = useTranslation('modules/common-media-cards');
|
||||
const { config } = useConfigContext();
|
||||
const [requestModalOpened, requestModal] = useDisclosure(false);
|
||||
|
||||
if (!config) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const service = config.apps.find((service) => service.integration.type === type);
|
||||
|
||||
const mediaUrl = movie.mediaInfo?.plexUrl ?? movie.mediaInfo?.mediaUrl;
|
||||
const serviceUrl = service?.behaviour.externalUrl ? service.behaviour.externalUrl : service?.url;
|
||||
|
||||
return (
|
||||
<Group noWrap style={{ maxHeight: 250 }} p={0} m={0} spacing="xs" align="stretch">
|
||||
<MantineImage
|
||||
withPlaceholder
|
||||
src={`https://image.tmdb.org/t/p/w600_and_h900_bestv2/${
|
||||
movie.posterPath ?? movie.backdropPath
|
||||
}`}
|
||||
height={200}
|
||||
width={150}
|
||||
radius="md"
|
||||
fit="cover"
|
||||
/>
|
||||
<Stack justify="space-between">
|
||||
<Stack spacing={4}>
|
||||
<Title lineClamp={2} order={5}>
|
||||
{movie.title ?? movie.name ?? movie.originalName}
|
||||
</Title>
|
||||
<Text color="dimmed" size="xs" lineClamp={4}>
|
||||
{movie.overview}
|
||||
</Text>
|
||||
</Stack>
|
||||
<Group spacing="xs">
|
||||
{!movie.mediaInfo?.mediaAddedAt && (
|
||||
<>
|
||||
<RequestModal
|
||||
base={movie}
|
||||
opened={requestModalOpened}
|
||||
setOpened={requestModal.toggle}
|
||||
/>
|
||||
<Button
|
||||
onClick={() => {
|
||||
requestModal.open();
|
||||
}}
|
||||
variant="light"
|
||||
size="sm"
|
||||
rightIcon={<IconDownload size={15} />}
|
||||
>
|
||||
{t('buttons.request')}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{mediaUrl && (
|
||||
<Button
|
||||
component="a"
|
||||
target="_blank"
|
||||
variant="outline"
|
||||
href={mediaUrl}
|
||||
size="sm"
|
||||
rightIcon={<IconPlayerPlay size={15} />}
|
||||
>
|
||||
{t('buttons.play')}
|
||||
</Button>
|
||||
)}
|
||||
{serviceUrl && (
|
||||
<Button
|
||||
component="a"
|
||||
target="_blank"
|
||||
href={serviceUrl}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
rightIcon={<IconExternalLink size={15} />}
|
||||
>
|
||||
TMDb
|
||||
</Button>
|
||||
)}
|
||||
</Group>
|
||||
</Stack>
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Autocomplete, Group, Kbd, Text, Tooltip, useMantineTheme } from '@mantine/core';
|
||||
import { useHotkeys } from '@mantine/hooks';
|
||||
import { Autocomplete, Group, Kbd, Modal, Text, Tooltip, useMantineTheme } from '@mantine/core';
|
||||
import { useDisclosure, useHotkeys } from '@mantine/hooks';
|
||||
import {
|
||||
IconBrandYoutube,
|
||||
IconDownload,
|
||||
@@ -8,10 +8,13 @@ import {
|
||||
IconWorld,
|
||||
TablerIconsProps,
|
||||
} from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { ReactNode, forwardRef, useMemo, useRef, useState } from 'react';
|
||||
import { useConfigContext } from '~/config/provider';
|
||||
import { api } from '~/utils/api';
|
||||
|
||||
import { MovieModal } from './MovieModal';
|
||||
|
||||
export const Search = () => {
|
||||
const [search, setSearch] = useState('');
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
@@ -19,6 +22,8 @@ export const Search = () => {
|
||||
const { data: userWithSettings } = api.user.getWithSettings.useQuery();
|
||||
const { config } = useConfigContext();
|
||||
const { colors } = useMantineTheme();
|
||||
const router = useRouter();
|
||||
const [showMovieModal, movieModal] = useDisclosure(router.query.movie === 'true');
|
||||
|
||||
const apps = useConfigApps(search);
|
||||
const engines = generateEngines(
|
||||
@@ -31,47 +36,61 @@ export const Search = () => {
|
||||
const data = [...engines, ...apps];
|
||||
|
||||
return (
|
||||
<Autocomplete
|
||||
ref={ref}
|
||||
radius="xl"
|
||||
w={400}
|
||||
variant="filled"
|
||||
placeholder="Search..."
|
||||
hoverOnSearchChange
|
||||
autoFocus={typeof window !== 'undefined' && window.innerWidth > 768}
|
||||
rightSection={
|
||||
<IconSearch
|
||||
onClick={() => ref.current?.focus()}
|
||||
color={colors.gray[5]}
|
||||
size={16}
|
||||
stroke={1.5}
|
||||
/>
|
||||
}
|
||||
limit={8}
|
||||
value={search}
|
||||
onChange={setSearch}
|
||||
data={data}
|
||||
itemComponent={SearchItemComponent}
|
||||
filter={(value, item: SearchAutoCompleteItem) =>
|
||||
engines.some((engine) => engine.sort === item.sort) ||
|
||||
item.value.toLowerCase().includes(value.trim().toLowerCase())
|
||||
}
|
||||
classNames={{
|
||||
input: 'dashboard-header-search-input',
|
||||
root: 'dashboard-header-search-root',
|
||||
}}
|
||||
onItemSubmit={(item: SearchAutoCompleteItem) => {
|
||||
setSearch('');
|
||||
if (item.sort === 'movie') {
|
||||
// TODO: show movie modal
|
||||
console.log('movie');
|
||||
return;
|
||||
<>
|
||||
<Autocomplete
|
||||
ref={ref}
|
||||
radius="xl"
|
||||
w={400}
|
||||
variant="filled"
|
||||
placeholder="Search..."
|
||||
hoverOnSearchChange
|
||||
autoFocus={typeof window !== 'undefined' && window.innerWidth > 768}
|
||||
rightSection={
|
||||
<IconSearch
|
||||
onClick={() => ref.current?.focus()}
|
||||
color={colors.gray[5]}
|
||||
size={16}
|
||||
stroke={1.5}
|
||||
/>
|
||||
}
|
||||
const target = userWithSettings?.settings.openSearchInNewTab ? '_blank' : '_self';
|
||||
window.open(item.metaData.url, target);
|
||||
}}
|
||||
aria-label="Search"
|
||||
/>
|
||||
limit={8}
|
||||
value={search}
|
||||
onChange={setSearch}
|
||||
data={data}
|
||||
itemComponent={SearchItemComponent}
|
||||
filter={(value, item: SearchAutoCompleteItem) =>
|
||||
engines.some((engine) => engine.sort === item.sort) ||
|
||||
item.value.toLowerCase().includes(value.trim().toLowerCase())
|
||||
}
|
||||
classNames={{
|
||||
input: 'dashboard-header-search-input',
|
||||
root: 'dashboard-header-search-root',
|
||||
}}
|
||||
onItemSubmit={(item: SearchAutoCompleteItem) => {
|
||||
setSearch('');
|
||||
if (item.sort === 'movie') {
|
||||
// TODO: show movie modal
|
||||
const url = new URL(`${window.location.origin}${router.asPath}`);
|
||||
url.searchParams.set('movie', 'true');
|
||||
url.searchParams.set('search', search);
|
||||
url.searchParams.set('type', item.value);
|
||||
router.push(url, undefined, { shallow: true });
|
||||
movieModal.open();
|
||||
return;
|
||||
}
|
||||
const target = userWithSettings?.settings.openSearchInNewTab ? '_blank' : '_self';
|
||||
window.open(item.metaData.url, target);
|
||||
}}
|
||||
aria-label="Search"
|
||||
/>
|
||||
<MovieModal
|
||||
opened={showMovieModal}
|
||||
closeModal={() => {
|
||||
movieModal.close();
|
||||
router.push(router.pathname, undefined, { shallow: true });
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { api } from '~/utils/api';
|
||||
|
||||
import { useColorTheme } from '../../tools/color';
|
||||
import { MovieResult } from './Movie.d';
|
||||
import { Result } from './SearchResult.d';
|
||||
import { Result } from './SearchResult';
|
||||
import { TvShowResult, TvShowResultSeason } from './TvShow.d';
|
||||
|
||||
interface RequestModalProps {
|
||||
@@ -70,7 +70,7 @@ export function MovieRequestModal({
|
||||
radius="lg"
|
||||
size="lg"
|
||||
trapFocus
|
||||
zIndex={150}
|
||||
zIndex={250}
|
||||
withinPortal
|
||||
opened={opened}
|
||||
title={
|
||||
@@ -155,6 +155,7 @@ export function TvRequestModal({
|
||||
onClose={() => setOpened(false)}
|
||||
radius="lg"
|
||||
size="lg"
|
||||
zIndex={250}
|
||||
opened={opened}
|
||||
title={
|
||||
<Group>
|
||||
|
||||
@@ -3,17 +3,18 @@ import axios from 'axios';
|
||||
import Consola from 'consola';
|
||||
import { z } from 'zod';
|
||||
import { MovieResult } from '~/modules/overseerr/Movie';
|
||||
import { Result } from '~/modules/overseerr/SearchResult';
|
||||
import { OriginalLanguage, Result } from '~/modules/overseerr/SearchResult';
|
||||
import { TvShowResult } from '~/modules/overseerr/TvShow';
|
||||
import { getConfig } from '~/tools/config/getConfig';
|
||||
|
||||
import { createTRPCRouter, publicProcedure } from '../trpc';
|
||||
|
||||
export const overseerrRouter = createTRPCRouter({
|
||||
all: publicProcedure
|
||||
search: publicProcedure
|
||||
.input(
|
||||
z.object({
|
||||
configName: z.string(),
|
||||
integration: z.enum(['overseerr', 'jellyseerr']),
|
||||
query: z.string().or(z.undefined()),
|
||||
limit: z.number().default(10),
|
||||
})
|
||||
@@ -21,9 +22,7 @@ export const overseerrRouter = createTRPCRouter({
|
||||
.query(async ({ input }) => {
|
||||
const config = getConfig(input.configName);
|
||||
|
||||
const app = config.apps.find(
|
||||
(app) => app.integration?.type === 'overseerr' || app.integration?.type === 'jellyseerr'
|
||||
);
|
||||
const app = config.apps.find((app) => app.integration?.type === input.integration);
|
||||
|
||||
if (input.query === '' || input.query === undefined) {
|
||||
return [];
|
||||
@@ -44,7 +43,7 @@ export const overseerrRouter = createTRPCRouter({
|
||||
'X-Api-Key': apiKey,
|
||||
},
|
||||
})
|
||||
.then((res) => res.data as Result[]);
|
||||
.then((res) => res.data.results as Result[]);
|
||||
|
||||
return data.slice(0, input.limit);
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user