Basic Jellyseerr discover page

This commit is contained in:
herrrta
2024-12-29 10:07:46 -05:00
parent c6b58c5c28
commit cbce83e109
8 changed files with 346 additions and 162 deletions

View File

@@ -34,6 +34,11 @@ import {useJellyseerr} from "@/hooks/useJellyseerr";
import {MovieResult, TvResult} from "@/utils/jellyseerr/server/models/Search"; import {MovieResult, TvResult} from "@/utils/jellyseerr/server/models/Search";
import {MediaType} from "@/utils/jellyseerr/server/constants/media"; import {MediaType} from "@/utils/jellyseerr/server/constants/media";
import JellyseerrPoster from "@/components/posters/JellyseerrPoster"; import JellyseerrPoster from "@/components/posters/JellyseerrPoster";
import {Tag} from "@/components/GenreTags";
import DiscoverSlide from "@/components/jellyseerr/DiscoverSlide";
import {sortBy} from "lodash";
type SearchType = 'Library' | 'Discover';
const exampleSearches = [ const exampleSearches = [
"Lord of the rings", "Lord of the rings",
@@ -50,6 +55,7 @@ export default function search() {
const { q, prev } = params as { q: string; prev: Href<string> }; const { q, prev } = params as { q: string; prev: Href<string> };
const [searchType, setSearchType] = useState<SearchType>("Library");
const [search, setSearch] = useState<string>(""); const [search, setSearch] = useState<string>("");
const [debouncedSearch] = useDebounce(search, 500); const [debouncedSearch] = useDebounce(search, 500);
@@ -138,10 +144,10 @@ export default function search() {
query: debouncedSearch, query: debouncedSearch,
types: ["Movie"], types: ["Movie"],
}), }),
enabled: debouncedSearch.length > 0, enabled: searchType === "Library" && debouncedSearch.length > 0,
}); });
const { data: jellyseerrResults, isFetching: r1 } = useQuery({ const { data: jellyseerrResults, isFetching: j1 } = useQuery({
queryKey: ["search", "jellyseerrResults", debouncedSearch], queryKey: ["search", "jellyseerrResults", debouncedSearch],
queryFn: async () => { queryFn: async () => {
const response = await jellyseerrApi?.search({ const response = await jellyseerrApi?.search({
@@ -152,7 +158,13 @@ export default function search() {
return response?.results; return response?.results;
}, },
enabled: !!jellyseerrApi && debouncedSearch.length > 0, enabled: !!jellyseerrApi && searchType === "Discover" && debouncedSearch.length > 0,
});
const { data: jellyseerrDiscoverSettings, isFetching: j2 } = useQuery({
queryKey: ["search", "jellyseerrDiscoverSettings", debouncedSearch],
queryFn: async () => jellyseerrApi?.discoverSettings(),
enabled: !!jellyseerrApi && searchType === "Discover" && debouncedSearch.length == 0,
}); });
const jellyseerrMovieResults: MovieResult[] | undefined = useMemo(() => const jellyseerrMovieResults: MovieResult[] | undefined = useMemo(() =>
@@ -172,7 +184,7 @@ export default function search() {
query: debouncedSearch, query: debouncedSearch,
types: ["Series"], types: ["Series"],
}), }),
enabled: debouncedSearch.length > 0, enabled: searchType === "Library" && debouncedSearch.length > 0,
}); });
const { data: episodes, isFetching: l3 } = useQuery({ const { data: episodes, isFetching: l3 } = useQuery({
@@ -182,7 +194,7 @@ export default function search() {
query: debouncedSearch, query: debouncedSearch,
types: ["Episode"], types: ["Episode"],
}), }),
enabled: debouncedSearch.length > 0, enabled: searchType === "Library" && debouncedSearch.length > 0,
}); });
const { data: collections, isFetching: l7 } = useQuery({ const { data: collections, isFetching: l7 } = useQuery({
@@ -192,7 +204,7 @@ export default function search() {
query: debouncedSearch, query: debouncedSearch,
types: ["BoxSet"], types: ["BoxSet"],
}), }),
enabled: debouncedSearch.length > 0, enabled: searchType === "Library" && debouncedSearch.length > 0,
}); });
const { data: actors, isFetching: l8 } = useQuery({ const { data: actors, isFetching: l8 } = useQuery({
@@ -202,7 +214,7 @@ export default function search() {
query: debouncedSearch, query: debouncedSearch,
types: ["Person"], types: ["Person"],
}), }),
enabled: debouncedSearch.length > 0, enabled: searchType === "Library" && debouncedSearch.length > 0,
}); });
const { data: artists, isFetching: l4 } = useQuery({ const { data: artists, isFetching: l4 } = useQuery({
@@ -212,7 +224,7 @@ export default function search() {
query: debouncedSearch, query: debouncedSearch,
types: ["MusicArtist"], types: ["MusicArtist"],
}), }),
enabled: debouncedSearch.length > 0, enabled: searchType === "Library" && debouncedSearch.length > 0,
}); });
const { data: albums, isFetching: l5 } = useQuery({ const { data: albums, isFetching: l5 } = useQuery({
@@ -222,7 +234,7 @@ export default function search() {
query: debouncedSearch, query: debouncedSearch,
types: ["MusicAlbum"], types: ["MusicAlbum"],
}), }),
enabled: debouncedSearch.length > 0, enabled: searchType === "Library" && debouncedSearch.length > 0,
}); });
const { data: songs, isFetching: l6 } = useQuery({ const { data: songs, isFetching: l6 } = useQuery({
@@ -232,7 +244,7 @@ export default function search() {
query: debouncedSearch, query: debouncedSearch,
types: ["Audio"], types: ["Audio"],
}), }),
enabled: debouncedSearch.length > 0, enabled: searchType === "Library" && debouncedSearch.length > 0,
}); });
const noResults = useMemo(() => { const noResults = useMemo(() => {
@@ -251,8 +263,8 @@ export default function search() {
}, [artists, episodes, albums, songs, movies, series, collections, actors, jellyseerrResults]); }, [artists, episodes, albums, songs, movies, series, collections, actors, jellyseerrResults]);
const loading = useMemo(() => { const loading = useMemo(() => {
return l1 || l2 || l3 || l4 || l5 || l6 || l7 || l8; return l1 || l2 || l3 || l4 || l5 || l6 || l7 || l8 || j1 || j2;
}, [l1, l2, l3, l4, l5, l6, l7, l8]); }, [l1, l2, l3, l4, l5, l6, l7, l8, j1, j2]);
return ( return (
<> <>
@@ -277,6 +289,18 @@ export default function search() {
/> />
</View> </View>
)} )}
{jellyseerrApi && (
<View className="flex flex-row flex-wrap space-x-2 px-4">
<TouchableOpacity onPress={() => setSearchType('Library')}>
<Tag text="Library" textClass="p-1"
className={searchType === "Library" ? "bg-neutral-600" : undefined}/>
</TouchableOpacity>
<TouchableOpacity onPress={() => setSearchType('Discover')}>
<Tag text="Discover" textClass="p-1"
className={searchType === "Discover" ? "bg-neutral-600" : undefined}/>
</TouchableOpacity>
</View>
)}
{!!q && ( {!!q && (
<View className="px-4 flex flex-col space-y-2"> <View className="px-4 flex flex-col space-y-2">
<Text className="text-neutral-500 "> <Text className="text-neutral-500 ">
@@ -284,144 +308,153 @@ export default function search() {
</Text> </Text>
</View> </View>
)} )}
<SearchItemWrapper {searchType === "Library" && (
header="Movies" <>
ids={movies?.map((m) => m.Id!)} <SearchItemWrapper
renderItem={(item: BaseItemDto) => ( header="Movies"
<TouchableItemRouter ids={movies?.map((m) => m.Id!)}
key={item.Id} renderItem={(item: BaseItemDto) => (
className="flex flex-col w-28 mr-2" <TouchableItemRouter
item={item} key={item.Id}
> className="flex flex-col w-28 mr-2"
<MoviePoster item={item} key={item.Id} /> item={item}
<Text numberOfLines={2} className="mt-2"> >
{item.Name} <MoviePoster item={item} key={item.Id}/>
</Text> <Text numberOfLines={2} className="mt-2">
<Text className="opacity-50 text-xs"> {item.Name}
{item.ProductionYear} </Text>
</Text> <Text className="opacity-50 text-xs">
</TouchableItemRouter> {item.ProductionYear}
)} </Text>
/> </TouchableItemRouter>
<SearchItemWrapper )}
header="Request Movies" />
items={jellyseerrMovieResults} <SearchItemWrapper
renderItem={(item: MovieResult) => ( ids={series?.map((m) => m.Id!)}
<JellyseerrPoster item={item} key={item.id} /> header="Series"
)} renderItem={(item: BaseItemDto) => (
/> <TouchableItemRouter
<SearchItemWrapper key={item.Id}
ids={series?.map((m) => m.Id!)} item={item}
header="Series" className="flex flex-col w-28 mr-2"
renderItem={(item: BaseItemDto) => ( >
<TouchableItemRouter <SeriesPoster item={item} key={item.Id}/>
key={item.Id} <Text numberOfLines={2} className="mt-2">
item={item} {item.Name}
className="flex flex-col w-28 mr-2" </Text>
> <Text className="opacity-50 text-xs">
<SeriesPoster item={item} key={item.Id} /> {item.ProductionYear}
<Text numberOfLines={2} className="mt-2"> </Text>
{item.Name} </TouchableItemRouter>
</Text> )}
<Text className="opacity-50 text-xs"> />
{item.ProductionYear} <SearchItemWrapper
</Text> ids={episodes?.map((m) => m.Id!)}
</TouchableItemRouter> header="Episodes"
)} renderItem={(item: BaseItemDto) => (
/> <TouchableItemRouter
<SearchItemWrapper item={item}
header="Request Series" key={item.Id}
items={jellyseerrTvResults} className="flex flex-col w-44 mr-2"
renderItem={(item: TvResult) => ( >
<JellyseerrPoster item={item} key={item.id} /> <ContinueWatchingPoster item={item}/>
)} <ItemCardText item={item}/>
/> </TouchableItemRouter>
<SearchItemWrapper )}
ids={episodes?.map((m) => m.Id!)} />
header="Episodes" <SearchItemWrapper
renderItem={(item: BaseItemDto) => ( ids={collections?.map((m) => m.Id!)}
<TouchableItemRouter header="Collections"
item={item} renderItem={(item: BaseItemDto) => (
key={item.Id} <TouchableItemRouter
className="flex flex-col w-44 mr-2" key={item.Id}
> item={item}
<ContinueWatchingPoster item={item} /> className="flex flex-col w-28 mr-2"
<ItemCardText item={item} /> >
</TouchableItemRouter> <MoviePoster item={item} key={item.Id}/>
)} <Text numberOfLines={2} className="mt-2">
/> {item.Name}
<SearchItemWrapper </Text>
ids={collections?.map((m) => m.Id!)} </TouchableItemRouter>
header="Collections" )}
renderItem={(item: BaseItemDto) => ( />
<TouchableItemRouter <SearchItemWrapper
key={item.Id} ids={actors?.map((m) => m.Id!)}
item={item} header="Actors"
className="flex flex-col w-28 mr-2" renderItem={(item: BaseItemDto) => (
> <TouchableItemRouter
<MoviePoster item={item} key={item.Id} /> item={item}
<Text numberOfLines={2} className="mt-2"> key={item.Id}
{item.Name} className="flex flex-col w-28 mr-2"
</Text> >
</TouchableItemRouter> <MoviePoster item={item}/>
)} <ItemCardText item={item}/>
/> </TouchableItemRouter>
<SearchItemWrapper )}
ids={actors?.map((m) => m.Id!)} />
header="Actors" <SearchItemWrapper
renderItem={(item: BaseItemDto) => ( ids={artists?.map((m) => m.Id!)}
<TouchableItemRouter header="Artists"
item={item} renderItem={(item: BaseItemDto) => (
key={item.Id} <TouchableItemRouter
className="flex flex-col w-28 mr-2" item={item}
> key={item.Id}
<MoviePoster item={item} /> className="flex flex-col w-28 mr-2"
<ItemCardText item={item} /> >
</TouchableItemRouter> <AlbumCover id={item.Id}/>
)} <ItemCardText item={item}/>
/> </TouchableItemRouter>
<SearchItemWrapper )}
ids={artists?.map((m) => m.Id!)} />
header="Artists" <SearchItemWrapper
renderItem={(item: BaseItemDto) => ( ids={albums?.map((m) => m.Id!)}
<TouchableItemRouter header="Albums"
item={item} renderItem={(item: BaseItemDto) => (
key={item.Id} <TouchableItemRouter
className="flex flex-col w-28 mr-2" item={item}
> key={item.Id}
<AlbumCover id={item.Id} /> className="flex flex-col w-28 mr-2"
<ItemCardText item={item} /> >
</TouchableItemRouter> <AlbumCover id={item.Id}/>
)} <ItemCardText item={item}/>
/> </TouchableItemRouter>
<SearchItemWrapper )}
ids={albums?.map((m) => m.Id!)} />
header="Albums" <SearchItemWrapper
renderItem={(item: BaseItemDto) => ( ids={songs?.map((m) => m.Id!)}
<TouchableItemRouter header="Songs"
item={item} renderItem={(item: BaseItemDto) => (
key={item.Id} <TouchableItemRouter
className="flex flex-col w-28 mr-2" item={item}
> key={item.Id}
<AlbumCover id={item.Id} /> className="flex flex-col w-28 mr-2"
<ItemCardText item={item} /> >
</TouchableItemRouter> <AlbumCover id={item.AlbumId}/>
)} <ItemCardText item={item}/>
/> </TouchableItemRouter>
<SearchItemWrapper )}
ids={songs?.map((m) => m.Id!)} />
header="Songs" </>
renderItem={(item: BaseItemDto) => ( )}
<TouchableItemRouter {searchType === "Discover" && (
item={item} <>
key={item.Id} <SearchItemWrapper
className="flex flex-col w-28 mr-2" header="Request Movies"
> items={jellyseerrMovieResults}
<AlbumCover id={item.AlbumId} /> renderItem={(item: MovieResult) => (
<ItemCardText item={item} /> <JellyseerrPoster item={item} key={item.id}/>
</TouchableItemRouter> )}
)} />
/> <SearchItemWrapper
header="Request Series"
items={jellyseerrTvResults}
renderItem={(item: TvResult) => (
<JellyseerrPoster item={item} key={item.id}/>
)}
/>
</>
)}
{loading ? ( {loading ? (
<View className="mt-4 flex justify-center items-center"> <View className="mt-4 flex justify-center items-center">
<Loader /> <Loader />
@@ -435,7 +468,7 @@ export default function search() {
"{debouncedSearch}" "{debouncedSearch}"
</Text> </Text>
</View> </View>
) : debouncedSearch.length === 0 ? ( ) : debouncedSearch.length === 0 && searchType === 'Library' ? (
<View className="mt-4 flex flex-col items-center space-y-2"> <View className="mt-4 flex flex-col items-center space-y-2">
{exampleSearches.map((e) => ( {exampleSearches.map((e) => (
<TouchableOpacity <TouchableOpacity
@@ -447,6 +480,12 @@ export default function search() {
</TouchableOpacity> </TouchableOpacity>
))} ))}
</View> </View>
) : debouncedSearch.length === 0 && searchType === 'Discover' ? (
<View className="mt-4 flex flex-col space-y-2 px-2">
{sortBy?.(jellyseerrDiscoverSettings?.filter(s => s.enabled), 'order')
.map((slide) => <DiscoverSlide key={slide.id} slide={slide}/>)
}
</View>
) : null} ) : null}
</View> </View>
</ScrollView> </ScrollView>

View File

@@ -1,2 +1,3 @@
export * from "./mmkv";
export * from "./number"; export * from "./number";
export * from "./mmkv"; export * from "./string";

View File

@@ -1,6 +1,9 @@
declare global { declare global {
interface Number { interface Number {
bytesToReadable(): string; bytesToReadable(): string;
secondsToMilliseconds(): number
minutesToMilliseconds(): number
hoursToMilliseconds(): number
} }
} }
@@ -19,4 +22,16 @@ Number.prototype.bytesToReadable = function () {
return `${bytes.toFixed(2)} B`; return `${bytes.toFixed(2)} B`;
} }
Number.prototype.secondsToMilliseconds = function () {
return this.valueOf() * 1000
}
Number.prototype.minutesToMilliseconds = function () {
return this.valueOf() * (60).secondsToMilliseconds()
}
Number.prototype.hoursToMilliseconds = function () {
return this.valueOf() * (60).minutesToMilliseconds()
}
export {}; export {};

16
augmentations/string.ts Normal file
View File

@@ -0,0 +1,16 @@
declare global {
interface String {
toTitle(): string;
}
}
String.prototype.toTitle = function () {
return this
.replaceAll("_", " ")
.replace(
/\w\S*/g,
text => text.charAt(0).toUpperCase() + text.substring(1).toLowerCase()
);
}
export {};

View File

@@ -8,14 +8,26 @@ interface TagProps {
textClass?: ViewProps["className"] textClass?: ViewProps["className"]
} }
export const Tag: React.FC<{ text: string, textClass?: ViewProps["className"]} & ViewProps> = ({
text,
textClass,
...props
}) => {
return (
<View className="bg-neutral-800 rounded-full px-2 py-1" {...props}>
<Text className={textClass}>{text}</Text>
</View>
);
};
export const Tags: React.FC<TagProps & ViewProps> = ({ tags, textClass = "text-xs", ...props }) => { export const Tags: React.FC<TagProps & ViewProps> = ({ tags, textClass = "text-xs", ...props }) => {
if (!tags || tags.length === 0) return null; if (!tags || tags.length === 0) return null;
return ( return (
<View className={`flex flex-row flex-wrap gap-1 ${props.className}`} {...props}> <View className={`flex flex-row flex-wrap gap-1 ${props.className}`} {...props}>
{tags.map((genre, idx) => ( {tags.map((tag, idx) => (
<View key={idx} className="bg-neutral-800 rounded-full px-2 py-1"> <View>
<Text className={textClass}>{genre}</Text> <Tag key={idx} textClass={textClass} text={tag}/>
</View> </View>
))} ))}
</View> </View>

View File

@@ -55,7 +55,9 @@ export const JellyserrRatings: React.FC<{result: MovieResult | TvResult}> = ({ r
? jellyseerrApi?.movieRatings(result.id) ? jellyseerrApi?.movieRatings(result.id)
: jellyseerrApi?.tvRatings(result.id) : jellyseerrApi?.tvRatings(result.id)
}, },
enabled: !!jellyseerrApi staleTime: (5).minutesToMilliseconds(),
retry: false,
enabled: !!jellyseerrApi,
}); });
return (isLoading || !!result.voteCount || return (isLoading || !!result.voteCount ||

View File

@@ -0,0 +1,75 @@
import React, {useMemo} from "react";
import DiscoverSlider from "@/utils/jellyseerr/server/entity/DiscoverSlider";
import {DiscoverSliderType} from "@/utils/jellyseerr/server/constants/discover";
import {DiscoverEndpoint, Endpoints, useJellyseerr} from "@/hooks/useJellyseerr";
import {useInfiniteQuery} from "@tanstack/react-query";
import {MovieResult, TvResult} from "@/utils/jellyseerr/server/models/Search";
import JellyseerrPoster from "@/components/posters/JellyseerrPoster";
import {Text} from "@/components/common/Text";
import {FlashList} from "@shopify/flash-list";
interface Props {
slide: DiscoverSlider
}
const DiscoverSlide: React.FC<Props> = ({slide}) => {
const {jellyseerrApi} = useJellyseerr();
const {data, isFetching, fetchNextPage, hasNextPage} = useInfiniteQuery({
queryKey: ["jellyseerr", "discover", slide.id],
queryFn: async ({ pageParam }) => {
let endpoint: DiscoverEndpoint | undefined = undefined;
let params: any = {
page: Number(pageParam)
}
switch (slide.type) {
case DiscoverSliderType.TRENDING:
endpoint = Endpoints.DISCOVER_TRENDING;
break;
case DiscoverSliderType.POPULAR_MOVIES:
case DiscoverSliderType.UPCOMING_MOVIES:
endpoint = Endpoints.DISCOVER_MOVIES
if (slide.type === DiscoverSliderType.UPCOMING_MOVIES)
params = { ...params, primaryReleaseDateGte: new Date().toISOString().split('T')[0]}
break;
case DiscoverSliderType.POPULAR_TV:
case DiscoverSliderType.UPCOMING_TV:
endpoint = Endpoints.DISCOVER_TV
if (slide.type === DiscoverSliderType.UPCOMING_TV)
params = {...params, firstAirDateGte: new Date().toISOString().split('T')[0]}
break;
}
return endpoint ? jellyseerrApi?.discover(endpoint, params) : null;
},
initialPageParam: 1,
getNextPageParam: (lastPage, pages) => ((lastPage?.page || pages?.findLast(p => p?.results.length)?.page) || 1) + 1,
enabled: !!jellyseerrApi,
staleTime: 0
});
const flatData = useMemo(() => data?.pages?.filter(p => p?.results.length).flatMap(p => p?.results), [data])
return (
(flatData && flatData?.length > 0) && <>
<Text className="font-bold text-lg mb-2">{DiscoverSliderType[slide.type].toString().toTitle()}</Text>
<FlashList
horizontal
showsHorizontalScrollIndicator={false}
keyExtractor={item => item!!.id.toString()}
estimatedItemSize={250}
data={flatData}
onEndReachedThreshold={1}
onEndReached={() => {
if (hasNextPage)
fetchNextPage()
}}
renderItem={({item}) =>
(item ? <JellyseerrPoster item={item as MovieResult | TvResult} /> : <></>)
}
/>
</>
)
}
export default DiscoverSlide;

View File

@@ -18,6 +18,7 @@ import {IssueStatus, IssueType} from "@/utils/jellyseerr/server/constants/issue"
import Issue from "@/utils/jellyseerr/server/entity/Issue"; import Issue from "@/utils/jellyseerr/server/entity/Issue";
import {RTRating} from "@/utils/jellyseerr/server/api/rating/rottentomatoes"; import {RTRating} from "@/utils/jellyseerr/server/api/rating/rottentomatoes";
import {writeErrorLog} from "@/utils/log"; import {writeErrorLog} from "@/utils/log";
import DiscoverSlider from "@/utils/jellyseerr/server/entity/DiscoverSlider";
interface SearchParams { interface SearchParams {
query: string, query: string,
@@ -27,8 +28,8 @@ interface SearchParams {
interface SearchResults { interface SearchResults {
page: number, page: number,
total_pages: number, totalPages: number,
total_results: number; totalResults: number;
results: Results[]; results: Results[];
} }
@@ -40,7 +41,7 @@ export const clearJellyseerrStorageData = () => {
storage.delete(JELLYSEERR_COOKIES); storage.delete(JELLYSEERR_COOKIES);
} }
enum Endpoints { export enum Endpoints {
STATUS = "/status", STATUS = "/status",
API_V1 = "/api/v1", API_V1 = "/api/v1",
SEARCH = "/search", SEARCH = "/search",
@@ -49,9 +50,16 @@ enum Endpoints {
RATINGS = "/ratings", RATINGS = "/ratings",
ISSUE = "/issue", ISSUE = "/issue",
TV = "/tv", TV = "/tv",
SETTINGS = "/settings",
DISCOVER = "/discover",
DISCOVER_TRENDING = DISCOVER + "/trending",
DISCOVER_MOVIES = DISCOVER + "/movies",
DISCOVER_TV = DISCOVER + TV,
AUTH_JELLYFIN = "/auth/jellyfin", AUTH_JELLYFIN = "/auth/jellyfin",
} }
export type DiscoverEndpoint = Endpoints.DISCOVER_TRENDING | Endpoints.DISCOVER_MOVIES | Endpoints.DISCOVER_TV;
export type TestResult = { export type TestResult = {
isValid: true; isValid: true;
requiresPass: boolean; requiresPass: boolean;
@@ -115,7 +123,9 @@ export class JellyseerrApi {
}; };
}) })
.catch((e) => { .catch((e) => {
console.error("Failed to test jellyseerr server url", e) const msg = "Failed to test jellyseerr server url";
toast.error(msg)
console.error(msg, e)
return { return {
isValid: false, isValid: false,
requiresPass: false requiresPass: false
@@ -137,6 +147,16 @@ export class JellyseerrApi {
}) })
} }
async discoverSettings(): Promise<DiscoverSlider[]> {
return this.axios?.get<DiscoverSlider[]>(Endpoints.API_V1 + Endpoints.SETTINGS + Endpoints.DISCOVER)
.then(({data}) => data)
}
async discover(endpoint: DiscoverEndpoint, params: any): Promise<SearchResults> {
return this.axios?.get<SearchResults>(Endpoints.API_V1 + endpoint, { params })
.then(({data}) => data)
}
async search(params: SearchParams): Promise<SearchResults> { async search(params: SearchParams): Promise<SearchResults> {
const response = await this.axios?.get<SearchResults>(Endpoints.API_V1 + Endpoints.SEARCH, {params}) const response = await this.axios?.get<SearchResults>(Endpoints.API_V1 + Endpoints.SEARCH, {params})
return response?.data return response?.data
@@ -204,15 +224,19 @@ export class JellyseerrApi {
return response; return response;
}, },
(error: AxiosError) => { (error: AxiosError) => {
const errorMsg = "Jellyseerr response error:"; const errorMsg = "Jellyseerr response error";
console.error(errorMsg, error, error.response?.data); console.error(errorMsg, error, error.response?.data);
writeErrorLog( writeErrorLog(
errorMsg + ` ${error.toString()}\n` + errorMsg + `\n` +
`error: ${error.toString()}\n` +
`url: ${error?.config?.url}\n` +
`data:\n` +
JSON.stringify(error.response?.data) JSON.stringify(error.response?.data)
); );
if (error.status === 403) { if (error.status === 403) {
clearJellyseerrStorageData() clearJellyseerrStorageData()
} }
return Promise.reject(error)
} }
); );