Files
aniplay-api/src/controllers/search/anilist.ts

84 lines
2.1 KiB
TypeScript

import { graphql } from "gql.tada";
import { GraphQLClient } from "graphql-request";
import { sleep } from "~/libs/sleep";
const SearchQuery = graphql(`
query Search($query: String!, $page: Int!, $limit: Int!) {
Page(page: $page, perPage: $limit) {
media(search: $query, type: ANIME, sort: [POPULARITY_DESC, SCORE_DESC]) {
id
title {
userPreferred
english
}
coverImage {
extraLarge
large
medium
}
}
pageInfo {
hasNextPage
}
}
}
`);
export async function fetchSearchResultsFromAnilist(
query: string,
page: number,
limit: number,
): Promise<SearchResultsResponse | undefined> {
const client = new GraphQLClient("https://graphql.anilist.co/");
return client
.request(SearchQuery, { page, query, limit })
.then((data) => data?.Page)
.then((page) => {
if (!page || page.media?.length === 0) {
return undefined;
}
const { media: results, pageInfo } = page;
return {
results: results?.map((result) => {
if (!result) return null;
return {
id: result.id,
title: result.title?.userPreferred ?? result.title?.english,
coverImage: result.coverImage,
};
}),
hasNextPage: pageInfo?.hasNextPage,
};
})
.catch((err) => {
const response = err.response;
if (response.status === 429) {
console.log("429, retrying in", response.headers.get("Retry-After"));
return sleep(Number(response.headers.get("Retry-After")!) * 1000).then(
() => fetchSearchResultsFromAnilist(query, page, limit),
);
}
throw err;
});
}
type SearchResultsResponse = {
results:
| ({
id: number;
title: { userPreferred: string | null; english: string | null } | null;
coverImage: {
extraLarge: string | null;
large: string | null;
medium: string | null;
} | null;
} | null)[]
| null;
hasNextPage: boolean | null | undefined;
};