feat: create route to search for titles
Summary: Test Plan:
This commit is contained in:
@@ -6,6 +6,6 @@ describe("Health Check", () => {
|
||||
it("should return { success: true }", async () => {
|
||||
const res = await app.request("/");
|
||||
|
||||
expect(await res.json()).toEqual({ success: true });
|
||||
expect(res.json()).resolves.toEqual({ success: true });
|
||||
});
|
||||
});
|
||||
|
||||
30
src/controllers/search/amvstrm.ts
Normal file
30
src/controllers/search/amvstrm.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
export async function fetchSearchResultsFromAmvstrm(
|
||||
query: string,
|
||||
page: number,
|
||||
) {
|
||||
return fetch(
|
||||
`https://api-amvstrm.nyt92.eu.org/api/v2/search?q=${query}&p=${page}`,
|
||||
)
|
||||
.then((res) => res.json<any>())
|
||||
.then(({ pageInfo: { hasNextPage }, results }) => ({
|
||||
hasNextPage,
|
||||
results: results.map(
|
||||
({
|
||||
id,
|
||||
title: { userPreferred, english },
|
||||
coverImage: { extraLarge, large, medium },
|
||||
}: any) => ({
|
||||
id,
|
||||
title: { userPreferred, english },
|
||||
coverImage: { extraLarge, large, medium },
|
||||
}),
|
||||
),
|
||||
}))
|
||||
.then((searchResults) => {
|
||||
if (searchResults.results.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return searchResults;
|
||||
});
|
||||
}
|
||||
43
src/controllers/search/anilist.ts
Normal file
43
src/controllers/search/anilist.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { graphql } from "gql.tada";
|
||||
import { GraphQLClient } from "graphql-request";
|
||||
|
||||
const SearchQuery = graphql(`
|
||||
query Search($query: String!, $page: Int!) {
|
||||
Page(page: $page) {
|
||||
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,
|
||||
) {
|
||||
const client = new GraphQLClient("https://graphql.anilist.co/");
|
||||
|
||||
return client
|
||||
.request(SearchQuery, { page, query })
|
||||
.then((data) => data?.Page)
|
||||
.then((page) => {
|
||||
if (!page || page.media?.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const { media: results, pageInfo } = page;
|
||||
return { results, hasNextPage: pageInfo?.hasNextPage };
|
||||
});
|
||||
}
|
||||
703
src/controllers/search/index.spec.ts
Normal file
703
src/controllers/search/index.spec.ts
Normal file
@@ -0,0 +1,703 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
|
||||
import app from "~/index";
|
||||
import { server } from "~/mocks";
|
||||
|
||||
server.listen();
|
||||
|
||||
describe('requests the "/search" route', () => {
|
||||
it("valid query that returns anilist results", async () => {
|
||||
const response = await app.request("/search?query=search query");
|
||||
|
||||
expect(response.json()).resolves.toEqual({
|
||||
success: true,
|
||||
results: [
|
||||
{
|
||||
id: 151807,
|
||||
title: {
|
||||
userPreferred: "Ore dake Level Up na Ken",
|
||||
english: "Solo Leveling",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx151807-yxY3olrjZH4k.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx151807-yxY3olrjZH4k.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx151807-yxY3olrjZH4k.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 2759,
|
||||
title: {
|
||||
userPreferred: "Evangelion Shin Movie: Jo",
|
||||
english: "Evangelion: 1.0 You Are (Not) Alone",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx2759-z07kq8Pnw5B1.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx2759-z07kq8Pnw5B1.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx2759-z07kq8Pnw5B1.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 139589,
|
||||
title: {
|
||||
userPreferred: "Kotarou wa Hitorigurashi",
|
||||
english: "Kotaro Lives Alone",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx139589-oFz7JwpwRkQV.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx139589-oFz7JwpwRkQV.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx139589-oFz7JwpwRkQV.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 145815,
|
||||
title: {
|
||||
userPreferred:
|
||||
"Noumin Kanren no Skill Bakka Agetetara Naze ka Tsuyoku Natta.",
|
||||
english:
|
||||
"I've Somehow Gotten Stronger When I Improved My Farm-Related Skills",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx145815-XsgcXy7WzgtK.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx145815-XsgcXy7WzgtK.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx145815-XsgcXy7WzgtK.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 176496,
|
||||
title: {
|
||||
userPreferred:
|
||||
"Ore dake Level Up na Ken: Season 2 - Arise from the Shadow",
|
||||
english: "Solo Leveling Season 2 -Arise from the Shadow-",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx176496-r6oXxEqdZL0n.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx176496-r6oXxEqdZL0n.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx176496-r6oXxEqdZL0n.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 1965,
|
||||
title: {
|
||||
userPreferred: "sola",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx1965-lWBpcTni9PS9.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx1965-lWBpcTni9PS9.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx1965-lWBpcTni9PS9.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 118123,
|
||||
title: {
|
||||
userPreferred: "Holo no Graffiti",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx118123-xqn5fYsjKXJU.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx118123-xqn5fYsjKXJU.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx118123-xqn5fYsjKXJU.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 2582,
|
||||
title: {
|
||||
userPreferred: "Soukou Kihei Votoms",
|
||||
english: "Armored Trooper Votoms",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx2582-aB1Vh1jDobQ3.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx2582-aB1Vh1jDobQ3.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx2582-aB1Vh1jDobQ3.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 116384,
|
||||
title: {
|
||||
userPreferred: "Sol Levante",
|
||||
english: "Sol Levante",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx116384-xn0nQAKGFSd7.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx116384-xn0nQAKGFSd7.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx116384-xn0nQAKGFSd7.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 104073,
|
||||
title: {
|
||||
userPreferred: "Sono Toki, Kanojo wa.",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/nx104073-OQ8YBTy7zmKf.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/nx104073-OQ8YBTy7zmKf.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/nx104073-OQ8YBTy7zmKf.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 15313,
|
||||
title: {
|
||||
userPreferred: "Wooser no Sono Higurashi",
|
||||
english: "Wooser's Hand-to-Mouth Life",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/15313.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/15313.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/15313.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 8068,
|
||||
title: {
|
||||
userPreferred: "Kuroshitsuji Picture Drama",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/8068.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/8068.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/8068.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 3174,
|
||||
title: {
|
||||
userPreferred: "sola Specials",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/3174.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/3174.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/3174.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 1443,
|
||||
title: {
|
||||
userPreferred: "SOL BIANCA",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/1443.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/1443.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/1443.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 153431,
|
||||
title: {
|
||||
userPreferred: "Onna no Sono no Hoshi",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx153431-DMBYQxagH3Uu.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx153431-DMBYQxagH3Uu.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx153431-DMBYQxagH3Uu.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 1444,
|
||||
title: {
|
||||
userPreferred: "Sol Bianca: Taiyou no Fune",
|
||||
english: "Sol Bianca: The Legacy",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx1444-7Yn6hmQ2bk9D.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx1444-7Yn6hmQ2bk9D.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx1444-7Yn6hmQ2bk9D.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 4138,
|
||||
title: {
|
||||
userPreferred: "Chiisana Pengin: Lolo no Bouken",
|
||||
english: "The Adventures of Scamper the Penguin",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/4138.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/4138.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/4138.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 164192,
|
||||
title: {
|
||||
userPreferred: "Planetarium",
|
||||
english: "Planetarium",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx164192-KQ8sYXbaAl6i.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx164192-KQ8sYXbaAl6i.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx164192-KQ8sYXbaAl6i.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 5838,
|
||||
title: {
|
||||
userPreferred: "Furudera no Obake-soudou",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/b5838-QTe07RRZylUm.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/b5838-QTe07RRZylUm.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/b5838-QTe07RRZylUm.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 162882,
|
||||
title: {
|
||||
userPreferred: "P.E.T.",
|
||||
english: "P.E.T.",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx162882-OQENM5pXn7QQ.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx162882-OQENM5pXn7QQ.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx162882-OQENM5pXn7QQ.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 102710,
|
||||
title: {
|
||||
userPreferred: "Kairaku no Sono",
|
||||
english: "The Garden of Pleasure",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/102710-dVayaOkzATwa.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/102710-dVayaOkzATwa.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/102710-dVayaOkzATwa.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 162881,
|
||||
title: {
|
||||
userPreferred: "Mosh Race",
|
||||
english: "Mosh Race",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx162881-c7xmNA6DlHFZ.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx162881-c7xmNA6DlHFZ.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx162881-c7xmNA6DlHFZ.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 5935,
|
||||
title: {
|
||||
userPreferred: "Marco Polo no Boken",
|
||||
english: "Marco Polo's Adventures",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/5935.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/5935.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/5935.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 103449,
|
||||
title: {
|
||||
userPreferred: "SOL",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/103449-FxDK8eJuMAKg.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/103449-FxDK8eJuMAKg.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/103449-FxDK8eJuMAKg.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 12993,
|
||||
title: {
|
||||
userPreferred: "Sono Mukou no Mukougawa",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/12993.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/12993.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/12993.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 20459,
|
||||
title: {
|
||||
userPreferred: "Ganbare! Lulu Lolo",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/20459.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/20459.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/20459.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 137760,
|
||||
title: {
|
||||
userPreferred: "Soko ni wa Mata Meikyuu",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/b137760-CleNdfmuKRy7.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/b137760-CleNdfmuKRy7.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/b137760-CleNdfmuKRy7.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 7473,
|
||||
title: {
|
||||
userPreferred: "Rennyo to Sono Haha",
|
||||
english: "Rennyo and His Mother",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/7473.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/7473.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/7473.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 21418,
|
||||
title: {
|
||||
userPreferred: "Ganbare! Lulu Lolo 3rd Season",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/21418-TZYwdItidowx.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/21418-TZYwdItidowx.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/21418-TZYwdItidowx.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 103517,
|
||||
title: {
|
||||
userPreferred: "Toute wa Sono Kotae",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/103517-XgOUryeFaPDW.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/103517-XgOUryeFaPDW.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/103517-XgOUryeFaPDW.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 113572,
|
||||
title: {
|
||||
userPreferred: "Sono Saki no Taniji",
|
||||
english: "Journey to the beyond",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/b113572-hP9x1SkRJXvA.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/b113572-hP9x1SkRJXvA.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/b113572-hP9x1SkRJXvA.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 20864,
|
||||
title: {
|
||||
userPreferred: "Ganbare! Lulu Lolo 2nd Season",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/20864.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/20864.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/20864.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 15129,
|
||||
title: {
|
||||
userPreferred: "Tanpen Animation Junpei Fujita",
|
||||
english: "Short Animations of Junpei Fujita",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/15129.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/15129.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/15129.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 106557,
|
||||
title: {
|
||||
userPreferred: "Sono Ie no Namae",
|
||||
english: "A Place to Name",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/nx106557-TPLmwa2EccB9.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/nx106557-TPLmwa2EccB9.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/nx106557-TPLmwa2EccB9.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 118133,
|
||||
title: {
|
||||
userPreferred: "Guzu no Soko",
|
||||
english: "In Inertia",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/b118133-y7RvDFmr30hZ.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/b118133-y7RvDFmr30hZ.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/b118133-y7RvDFmr30hZ.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 169686,
|
||||
title: {
|
||||
userPreferred: "Soto ni Denai hi",
|
||||
english: "Indoor Days",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx169686-exScHzB5UX2D.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx169686-exScHzB5UX2D.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx169686-exScHzB5UX2D.jpg",
|
||||
},
|
||||
},
|
||||
],
|
||||
hasNextPage: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("valid query that returns amvstrm results", async () => {
|
||||
const response = await app.request("/search?query=amvstrm");
|
||||
|
||||
expect(response.json()).resolves.toEqual({
|
||||
hasNextPage: false,
|
||||
success: true,
|
||||
results: [
|
||||
{
|
||||
id: 21703,
|
||||
title: {
|
||||
userPreferred: "Fune wo Amu",
|
||||
english: "The Great Passage",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx21703-rXiXsRgiIO12.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx21703-rXiXsRgiIO12.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx21703-rXiXsRgiIO12.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 120607,
|
||||
title: {
|
||||
userPreferred: "Ame wo Matsu,",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx120607-XujIeO90fbi7.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx120607-XujIeO90fbi7.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx120607-XujIeO90fbi7.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 3239,
|
||||
title: {
|
||||
userPreferred: "Cream Lemon",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/nx3239-QbcjUJhHIjY1.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/nx3239-QbcjUJhHIjY1.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/nx3239-QbcjUJhHIjY1.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 116329,
|
||||
title: {
|
||||
userPreferred: "Ame to Cappuccino ",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx116329-kDQOjwtu20XB.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx116329-kDQOjwtu20XB.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx116329-kDQOjwtu20XB.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 5609,
|
||||
title: {
|
||||
userPreferred: "Emi to Yobanaide",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/5609.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/5609.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/5609.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 148835,
|
||||
title: {
|
||||
userPreferred: "Ame, nochi",
|
||||
english: "After the rain",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx148835-zKiES8icWZXc.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx148835-zKiES8icWZXc.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx148835-zKiES8icWZXc.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 151091,
|
||||
title: {
|
||||
userPreferred: "Ichigo Ame",
|
||||
english: "Strawberry Candy",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx151091-3MLlHlbzQUfk.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx151091-3MLlHlbzQUfk.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx151091-3MLlHlbzQUfk.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 148622,
|
||||
title: {
|
||||
userPreferred: "Ame yo Ame yo Fure",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx148622-Ghhr815fNoo1.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx148622-Ghhr815fNoo1.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx148622-Ghhr815fNoo1.png",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("query that returns no results", async () => {
|
||||
const response = await app.request("/search?query=");
|
||||
|
||||
expect(response.json()).resolves.toEqual({
|
||||
success: false,
|
||||
results: [],
|
||||
hasNextPage: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
56
src/controllers/search/index.ts
Normal file
56
src/controllers/search/index.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi";
|
||||
|
||||
import { fetchFromMultipleSources } from "~/libs/fetchFromMultipleSources";
|
||||
import { PaginatedResponseSchema } from "~/types/schema";
|
||||
import { Title } from "~/types/title";
|
||||
|
||||
import { fetchSearchResultsFromAmvstrm } from "./amvstrm";
|
||||
import { fetchSearchResultsFromAnilist } from "./anilist";
|
||||
import { SearchResult } from "./searchResult";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
const route = createRoute({
|
||||
tags: ["aniplay", "title"],
|
||||
operationId: "search",
|
||||
summary: "Search for a title",
|
||||
method: "get",
|
||||
path: "/",
|
||||
request: {
|
||||
query: z.object({ query: z.string(), page: z.number().min(1).default(1) }),
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: PaginatedResponseSchema(SearchResult),
|
||||
},
|
||||
},
|
||||
description: "Returns a list of paginated results for the query",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
app.openapi(route, async (c) => {
|
||||
const query = c.req.query("query") ?? "";
|
||||
const page = Number(c.req.query("page") ?? 1);
|
||||
|
||||
const response = await fetchFromMultipleSources([
|
||||
() => fetchSearchResultsFromAnilist(query, page),
|
||||
() => fetchSearchResultsFromAmvstrm(query, page),
|
||||
]);
|
||||
if (!response) {
|
||||
return c.json({ success: false, results: [], hasNextPage: false }, 200);
|
||||
}
|
||||
|
||||
return c.json(
|
||||
{
|
||||
success: true,
|
||||
results: response.results,
|
||||
hasNextPage: response.hasNextPage ?? false,
|
||||
},
|
||||
200,
|
||||
);
|
||||
});
|
||||
|
||||
export default app;
|
||||
18
src/controllers/search/searchResult.ts
Normal file
18
src/controllers/search/searchResult.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { z } from "@hono/zod-openapi";
|
||||
|
||||
export const SearchResult = z.object({
|
||||
id: z.number().openapi({ type: "integer", format: "int64" }),
|
||||
title: z.nullable(
|
||||
z.object({
|
||||
userPreferred: z.nullable(z.string()),
|
||||
english: z.nullable(z.string()),
|
||||
}),
|
||||
),
|
||||
coverImage: z.nullable(
|
||||
z.object({
|
||||
medium: z.nullable(z.string()).optional(),
|
||||
large: z.nullable(z.string()).optional(),
|
||||
extraLarge: z.nullable(z.string()).optional(),
|
||||
}),
|
||||
),
|
||||
});
|
||||
@@ -5,7 +5,7 @@ export async function fetchTitleFromAmvstrm(
|
||||
): Promise<Title | undefined> {
|
||||
return Promise.all([
|
||||
fetch(`https://api-amvstrm.nyt92.eu.org/api/v2/info/${aniListId}`).then(
|
||||
(res) => res.json() as Promise<any>,
|
||||
(res) => res.json<any>(),
|
||||
),
|
||||
fetchMissingInformationFromAnify(aniListId).catch((err) => {
|
||||
console.error("Failed to get missing information from Anify", err);
|
||||
|
||||
@@ -13,6 +13,10 @@ app.route(
|
||||
"/title",
|
||||
await import("~/controllers/title").then((controller) => controller.default),
|
||||
);
|
||||
app.route(
|
||||
"/search",
|
||||
await import("~/controllers/search").then((controller) => controller.default),
|
||||
);
|
||||
|
||||
// The OpenAPI documentation will be available at /doc
|
||||
app.doc("/openapi.json", {
|
||||
|
||||
2359
src/mocks/amvstrm/search.ts
Normal file
2359
src/mocks/amvstrm/search.ts
Normal file
File diff suppressed because it is too large
Load Diff
575
src/mocks/anilist/search.ts
Normal file
575
src/mocks/anilist/search.ts
Normal file
@@ -0,0 +1,575 @@
|
||||
import { HttpResponse, graphql } from "msw";
|
||||
|
||||
export function getAnilistSearchResults() {
|
||||
return graphql.query("Search", ({ variables: { query, page } }) => {
|
||||
console.log(`Intercepting Search query with ${query} and page ${page}`);
|
||||
|
||||
if (!query || query === "amvstrm") {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
Page: {
|
||||
media: [],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
Page: {
|
||||
media: [
|
||||
{
|
||||
id: 151807,
|
||||
title: {
|
||||
userPreferred: "Ore dake Level Up na Ken",
|
||||
english: "Solo Leveling",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx151807-yxY3olrjZH4k.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx151807-yxY3olrjZH4k.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx151807-yxY3olrjZH4k.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 2759,
|
||||
title: {
|
||||
userPreferred: "Evangelion Shin Movie: Jo",
|
||||
english: "Evangelion: 1.0 You Are (Not) Alone",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx2759-z07kq8Pnw5B1.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx2759-z07kq8Pnw5B1.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx2759-z07kq8Pnw5B1.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 139589,
|
||||
title: {
|
||||
userPreferred: "Kotarou wa Hitorigurashi",
|
||||
english: "Kotaro Lives Alone",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx139589-oFz7JwpwRkQV.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx139589-oFz7JwpwRkQV.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx139589-oFz7JwpwRkQV.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 145815,
|
||||
title: {
|
||||
userPreferred:
|
||||
"Noumin Kanren no Skill Bakka Agetetara Naze ka Tsuyoku Natta.",
|
||||
english:
|
||||
"I've Somehow Gotten Stronger When I Improved My Farm-Related Skills",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx145815-XsgcXy7WzgtK.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx145815-XsgcXy7WzgtK.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx145815-XsgcXy7WzgtK.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 176496,
|
||||
title: {
|
||||
userPreferred:
|
||||
"Ore dake Level Up na Ken: Season 2 - Arise from the Shadow",
|
||||
english: "Solo Leveling Season 2 -Arise from the Shadow-",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx176496-r6oXxEqdZL0n.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx176496-r6oXxEqdZL0n.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx176496-r6oXxEqdZL0n.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 1965,
|
||||
title: {
|
||||
userPreferred: "sola",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx1965-lWBpcTni9PS9.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx1965-lWBpcTni9PS9.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx1965-lWBpcTni9PS9.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 118123,
|
||||
title: {
|
||||
userPreferred: "Holo no Graffiti",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx118123-xqn5fYsjKXJU.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx118123-xqn5fYsjKXJU.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx118123-xqn5fYsjKXJU.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 2582,
|
||||
title: {
|
||||
userPreferred: "Soukou Kihei Votoms",
|
||||
english: "Armored Trooper Votoms",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx2582-aB1Vh1jDobQ3.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx2582-aB1Vh1jDobQ3.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx2582-aB1Vh1jDobQ3.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 116384,
|
||||
title: {
|
||||
userPreferred: "Sol Levante",
|
||||
english: "Sol Levante",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx116384-xn0nQAKGFSd7.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx116384-xn0nQAKGFSd7.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx116384-xn0nQAKGFSd7.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 104073,
|
||||
title: {
|
||||
userPreferred: "Sono Toki, Kanojo wa.",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/nx104073-OQ8YBTy7zmKf.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/nx104073-OQ8YBTy7zmKf.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/nx104073-OQ8YBTy7zmKf.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 15313,
|
||||
title: {
|
||||
userPreferred: "Wooser no Sono Higurashi",
|
||||
english: "Wooser's Hand-to-Mouth Life",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/15313.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/15313.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/15313.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 8068,
|
||||
title: {
|
||||
userPreferred: "Kuroshitsuji Picture Drama",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/8068.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/8068.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/8068.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 3174,
|
||||
title: {
|
||||
userPreferred: "sola Specials",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/3174.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/3174.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/3174.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 1443,
|
||||
title: {
|
||||
userPreferred: "SOL BIANCA",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/1443.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/1443.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/1443.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 153431,
|
||||
title: {
|
||||
userPreferred: "Onna no Sono no Hoshi",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx153431-DMBYQxagH3Uu.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx153431-DMBYQxagH3Uu.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx153431-DMBYQxagH3Uu.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 1444,
|
||||
title: {
|
||||
userPreferred: "Sol Bianca: Taiyou no Fune",
|
||||
english: "Sol Bianca: The Legacy",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx1444-7Yn6hmQ2bk9D.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx1444-7Yn6hmQ2bk9D.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx1444-7Yn6hmQ2bk9D.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 4138,
|
||||
title: {
|
||||
userPreferred: "Chiisana Pengin: Lolo no Bouken",
|
||||
english: "The Adventures of Scamper the Penguin",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/4138.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/4138.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/4138.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 164192,
|
||||
title: {
|
||||
userPreferred: "Planetarium",
|
||||
english: "Planetarium",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx164192-KQ8sYXbaAl6i.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx164192-KQ8sYXbaAl6i.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx164192-KQ8sYXbaAl6i.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 5838,
|
||||
title: {
|
||||
userPreferred: "Furudera no Obake-soudou",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/b5838-QTe07RRZylUm.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/b5838-QTe07RRZylUm.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/b5838-QTe07RRZylUm.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 162882,
|
||||
title: {
|
||||
userPreferred: "P.E.T.",
|
||||
english: "P.E.T.",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx162882-OQENM5pXn7QQ.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx162882-OQENM5pXn7QQ.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx162882-OQENM5pXn7QQ.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 102710,
|
||||
title: {
|
||||
userPreferred: "Kairaku no Sono",
|
||||
english: "The Garden of Pleasure",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/102710-dVayaOkzATwa.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/102710-dVayaOkzATwa.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/102710-dVayaOkzATwa.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 162881,
|
||||
title: {
|
||||
userPreferred: "Mosh Race",
|
||||
english: "Mosh Race",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx162881-c7xmNA6DlHFZ.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx162881-c7xmNA6DlHFZ.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx162881-c7xmNA6DlHFZ.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 5935,
|
||||
title: {
|
||||
userPreferred: "Marco Polo no Boken",
|
||||
english: "Marco Polo's Adventures",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/5935.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/5935.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/5935.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 103449,
|
||||
title: {
|
||||
userPreferred: "SOL",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/103449-FxDK8eJuMAKg.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/103449-FxDK8eJuMAKg.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/103449-FxDK8eJuMAKg.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 12993,
|
||||
title: {
|
||||
userPreferred: "Sono Mukou no Mukougawa",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/12993.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/12993.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/12993.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 20459,
|
||||
title: {
|
||||
userPreferred: "Ganbare! Lulu Lolo",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/20459.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/20459.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/20459.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 137760,
|
||||
title: {
|
||||
userPreferred: "Soko ni wa Mata Meikyuu",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/b137760-CleNdfmuKRy7.png",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/b137760-CleNdfmuKRy7.png",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/b137760-CleNdfmuKRy7.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 7473,
|
||||
title: {
|
||||
userPreferred: "Rennyo to Sono Haha",
|
||||
english: "Rennyo and His Mother",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/7473.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/7473.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/7473.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 21418,
|
||||
title: {
|
||||
userPreferred: "Ganbare! Lulu Lolo 3rd Season",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/21418-TZYwdItidowx.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/21418-TZYwdItidowx.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/21418-TZYwdItidowx.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 103517,
|
||||
title: {
|
||||
userPreferred: "Toute wa Sono Kotae",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/103517-XgOUryeFaPDW.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/103517-XgOUryeFaPDW.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/103517-XgOUryeFaPDW.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 113572,
|
||||
title: {
|
||||
userPreferred: "Sono Saki no Taniji",
|
||||
english: "Journey to the beyond",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/b113572-hP9x1SkRJXvA.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/b113572-hP9x1SkRJXvA.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/b113572-hP9x1SkRJXvA.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 20864,
|
||||
title: {
|
||||
userPreferred: "Ganbare! Lulu Lolo 2nd Season",
|
||||
english: null,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/20864.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/20864.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/20864.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 15129,
|
||||
title: {
|
||||
userPreferred: "Tanpen Animation Junpei Fujita",
|
||||
english: "Short Animations of Junpei Fujita",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/15129.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/15129.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/15129.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 106557,
|
||||
title: {
|
||||
userPreferred: "Sono Ie no Namae",
|
||||
english: "A Place to Name",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/nx106557-TPLmwa2EccB9.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/nx106557-TPLmwa2EccB9.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/nx106557-TPLmwa2EccB9.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 118133,
|
||||
title: {
|
||||
userPreferred: "Guzu no Soko",
|
||||
english: "In Inertia",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/b118133-y7RvDFmr30hZ.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/b118133-y7RvDFmr30hZ.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/b118133-y7RvDFmr30hZ.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 169686,
|
||||
title: {
|
||||
userPreferred: "Soto ni Denai hi",
|
||||
english: "Indoor Days",
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx169686-exScHzB5UX2D.jpg",
|
||||
large:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx169686-exScHzB5UX2D.jpg",
|
||||
medium:
|
||||
"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx169686-exScHzB5UX2D.jpg",
|
||||
},
|
||||
},
|
||||
],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,5 +1,13 @@
|
||||
import { getAmvstrmSearchResults } from "./amvstrm/search";
|
||||
import { getAmvstrmTitle } from "./amvstrm/title";
|
||||
import { getAnifyTitle } from "./anify/title";
|
||||
import { getAnilistSearchResults } from "./anilist/search";
|
||||
import { getAnilistTitle } from "./anilist/title";
|
||||
|
||||
export const handlers = [getAnilistTitle(), getAmvstrmTitle(), getAnifyTitle()];
|
||||
export const handlers = [
|
||||
getAnilistSearchResults(),
|
||||
getAnilistTitle(),
|
||||
getAmvstrmSearchResults(),
|
||||
getAmvstrmTitle(),
|
||||
getAnifyTitle(),
|
||||
];
|
||||
|
||||
@@ -9,6 +9,14 @@ export const SuccessResponseSchema = <T extends ZodSchema>(schema?: T) => {
|
||||
return z.object({ success: z.literal(true), result: schema });
|
||||
};
|
||||
|
||||
export const PaginatedResponseSchema = <T extends ZodSchema>(schema: T) => {
|
||||
return z.object({
|
||||
success: z.boolean(),
|
||||
results: z.array(schema),
|
||||
hasNextPage: z.boolean(),
|
||||
});
|
||||
};
|
||||
|
||||
export const ErrorResponseSchema = z.object({
|
||||
success: z.literal(false),
|
||||
});
|
||||
|
||||
@@ -56,5 +56,5 @@ export const Title = z.object({
|
||||
}),
|
||||
),
|
||||
idMal: z.number().nullable(),
|
||||
id: z.number(),
|
||||
id: z.number().openapi({ type: "integer", format: "int64" }),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user