From 88b1a4ced58c3589f3ee5cfa8a52ed466fadf3ad Mon Sep 17 00:00:00 2001 From: Rushil Perera Date: Thu, 16 May 2024 01:21:06 -0400 Subject: [PATCH] feat: create route to search for titles Summary: Test Plan: --- src/controllers/health-check/index.spec.ts | 2 +- src/controllers/search/amvstrm.ts | 30 + src/controllers/search/anilist.ts | 43 + src/controllers/search/index.spec.ts | 703 ++++++ src/controllers/search/index.ts | 56 + src/controllers/search/searchResult.ts | 18 + src/controllers/title/amvstrm.ts | 2 +- src/index.ts | 4 + src/mocks/amvstrm/search.ts | 2359 ++++++++++++++++++++ src/mocks/anilist/search.ts | 575 +++++ src/mocks/handlers.ts | 10 +- src/types/schema.ts | 8 + src/types/title/index.ts | 2 +- 13 files changed, 3808 insertions(+), 4 deletions(-) create mode 100644 src/controllers/search/amvstrm.ts create mode 100644 src/controllers/search/anilist.ts create mode 100644 src/controllers/search/index.spec.ts create mode 100644 src/controllers/search/index.ts create mode 100644 src/controllers/search/searchResult.ts create mode 100644 src/mocks/amvstrm/search.ts create mode 100644 src/mocks/anilist/search.ts diff --git a/src/controllers/health-check/index.spec.ts b/src/controllers/health-check/index.spec.ts index 9ed2290..921ca43 100644 --- a/src/controllers/health-check/index.spec.ts +++ b/src/controllers/health-check/index.spec.ts @@ -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 }); }); }); diff --git a/src/controllers/search/amvstrm.ts b/src/controllers/search/amvstrm.ts new file mode 100644 index 0000000..04b6ee9 --- /dev/null +++ b/src/controllers/search/amvstrm.ts @@ -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()) + .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; + }); +} diff --git a/src/controllers/search/anilist.ts b/src/controllers/search/anilist.ts new file mode 100644 index 0000000..6a0e0dc --- /dev/null +++ b/src/controllers/search/anilist.ts @@ -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 }; + }); +} diff --git a/src/controllers/search/index.spec.ts b/src/controllers/search/index.spec.ts new file mode 100644 index 0000000..35575e9 --- /dev/null +++ b/src/controllers/search/index.spec.ts @@ -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, + }); + }); +}); diff --git a/src/controllers/search/index.ts b/src/controllers/search/index.ts new file mode 100644 index 0000000..c0c5504 --- /dev/null +++ b/src/controllers/search/index.ts @@ -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; diff --git a/src/controllers/search/searchResult.ts b/src/controllers/search/searchResult.ts new file mode 100644 index 0000000..d7dedc1 --- /dev/null +++ b/src/controllers/search/searchResult.ts @@ -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(), + }), + ), +}); diff --git a/src/controllers/title/amvstrm.ts b/src/controllers/title/amvstrm.ts index ee65ecc..8756dd7 100644 --- a/src/controllers/title/amvstrm.ts +++ b/src/controllers/title/amvstrm.ts @@ -5,7 +5,7 @@ export async function fetchTitleFromAmvstrm( ): Promise { 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); diff --git a/src/index.ts b/src/index.ts index 8657749..4babaed 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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", { diff --git a/src/mocks/amvstrm/search.ts b/src/mocks/amvstrm/search.ts new file mode 100644 index 0000000..a02c994 --- /dev/null +++ b/src/mocks/amvstrm/search.ts @@ -0,0 +1,2359 @@ +import { HttpResponse, http } from "msw"; + +export function getAmvstrmSearchResults() { + return http.get( + "https://api-amvstrm.nyt92.eu.org/api/v2/search", + ({ request: { url: urlString } }) => { + const url = new URL(urlString); + const query = url.searchParams.get("q"); + + if (!query) { + return HttpResponse.json({ + code: 200, + message: "success", + pageInfo: { + total: 0, + perPage: 50, + currentPage: 1, + lastPage: 1, + hasNextPage: false, + }, + results: [], + }); + } + + if (query === "amvstrm") { + return HttpResponse.json({ + code: 200, + message: "success", + pageInfo: { + total: 8, + perPage: 50, + currentPage: 1, + lastPage: 1, + hasNextPage: false, + }, + results: [ + { + id: 21703, + idMal: 32948, + status: "FINISHED", + title: { + userPreferred: "Fune wo Amu", + romaji: "Fune wo Amu", + english: "The Great Passage", + native: "舟を編む", + }, + bannerImage: + "https://s4.anilist.co/file/anilistcdn/media/anime/banner/21703-3JiMHroUgMTB.jpg", + 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", + color: null, + }, + episodes: 11, + genres: ["Drama", "Romance", "Slice of Life"], + tags: [ + { + id: 109, + name: "Primarily Adult Cast", + }, + { + id: 145, + name: "Work", + }, + { + id: 394, + name: "Writing", + }, + { + id: 82, + name: "Male Protagonist", + }, + { + id: 153, + name: "Time Skip", + }, + { + id: 391, + name: "Philosophy", + }, + { + id: 653, + name: "Office Lady", + }, + { + id: 102, + name: "Coming of Age", + }, + { + id: 140, + name: "Educational", + }, + { + id: 1045, + name: "Heterosexual", + }, + { + id: 1433, + name: "Marriage", + }, + { + id: 144, + name: "Meta", + }, + { + id: 1571, + name: "Office", + }, + { + id: 75, + name: "Boys' Love", + }, + ], + season: "FALL", + format: "TV", + seasonYear: 2016, + averageScore: 74, + nextAiringEpisode: null, + }, + { + id: 120607, + idMal: 42344, + status: "FINISHED", + title: { + userPreferred: "Ame wo Matsu,", + romaji: "Ame wo Matsu,", + english: null, + native: "アメヲマツ、", + }, + bannerImage: + "https://s4.anilist.co/file/anilistcdn/media/anime/banner/120607-PB94qJKEMqhO.jpg", + 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", + color: "#50a1e4", + }, + episodes: 1, + genres: [], + tags: [ + { + id: 595, + name: "Urban", + }, + { + id: 98, + name: "Female Protagonist", + }, + { + id: 82, + name: "Male Protagonist", + }, + { + id: 195, + name: "Photography", + }, + ], + season: null, + format: "MUSIC", + seasonYear: null, + averageScore: 76, + nextAiringEpisode: null, + }, + { + id: 3239, + idMal: 3239, + status: "FINISHED", + title: { + userPreferred: "Cream Lemon", + romaji: "Cream Lemon", + english: null, + native: "くりいむレモン", + }, + bannerImage: + "https://s4.anilist.co/file/anilistcdn/media/anime/banner/3239-4q9kPd0GNC7e.jpg", + 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", + color: "#e47850", + }, + episodes: 38, + genres: ["Comedy", "Hentai"], + tags: [ + { + id: 471, + name: "Anthology", + }, + { + id: 100, + name: "Nudity", + }, + { + id: 135, + name: "Cunnilingus", + }, + { + id: 39, + name: "Parody", + }, + { + id: 1045, + name: "Heterosexual", + }, + { + id: 76, + name: "Yuri", + }, + { + id: 193, + name: "Episodic", + }, + { + id: 446, + name: "Primarily Child Cast", + }, + { + id: 128, + name: "Incest", + }, + { + id: 483, + name: "LGBTQ+ Themes", + }, + { + id: 138, + name: "Age Gap", + }, + { + id: 131, + name: "Masturbation", + }, + { + id: 173, + name: "Motorcycles", + }, + { + id: 132, + name: "Threesome", + }, + { + id: 46, + name: "School", + }, + { + id: 133, + name: "Sex Toys", + }, + { + id: 246, + name: "Bondage", + }, + { + id: 134, + name: "Fellatio", + }, + { + id: 294, + name: "Bisexual", + }, + ], + season: "SUMMER", + format: "OVA", + seasonYear: 1984, + averageScore: 55, + nextAiringEpisode: null, + }, + { + id: 116329, + idMal: 41180, + status: "FINISHED", + title: { + userPreferred: "Ame to Cappuccino ", + romaji: "Ame to Cappuccino ", + english: null, + native: "雨とカプチーノ", + }, + bannerImage: + "https://s4.anilist.co/file/anilistcdn/media/anime/banner/116329-ZiY7ghINEwwa.jpg", + 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", + color: "#e45028", + }, + episodes: 1, + genres: [], + tags: [ + { + id: 89, + name: "Full CGI", + }, + ], + season: null, + format: "MUSIC", + seasonYear: null, + averageScore: 65, + nextAiringEpisode: null, + }, + { + id: 5609, + idMal: 5609, + status: "FINISHED", + title: { + userPreferred: "Emi to Yobanaide", + romaji: "Emi to Yobanaide", + english: null, + native: "エイミーと呼ばないでっ", + }, + bannerImage: 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", + color: "#e4931a", + }, + episodes: 2, + genres: ["Hentai"], + tags: [ + { + id: 536, + name: "Dissociative Identities", + }, + { + id: 1045, + name: "Heterosexual", + }, + { + id: 1102, + name: "Inseki", + }, + { + id: 1146, + name: "Femdom", + }, + { + id: 76, + name: "Yuri", + }, + { + id: 98, + name: "Female Protagonist", + }, + { + id: 246, + name: "Bondage", + }, + { + id: 830, + name: "Group Sex", + }, + { + id: 317, + name: "Handjob", + }, + { + id: 134, + name: "Fellatio", + }, + { + id: 135, + name: "Cunnilingus", + }, + ], + season: "WINTER", + format: "OVA", + seasonYear: 1997, + averageScore: 42, + nextAiringEpisode: null, + }, + { + id: 148835, + idMal: null, + status: "FINISHED", + title: { + userPreferred: "Ame, nochi", + romaji: "Ame, nochi", + english: "After the rain", + native: "雨、のち", + }, + bannerImage: + "https://s4.anilist.co/file/anilistcdn/media/anime/banner/148835-viLNTvYnqLrC.jpg", + 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", + color: "#e4e45d", + }, + episodes: 1, + genres: ["Slice of Life"], + tags: [], + season: null, + format: "MOVIE", + seasonYear: null, + averageScore: null, + nextAiringEpisode: null, + }, + { + id: 151091, + idMal: 54012, + status: "FINISHED", + title: { + userPreferred: "Ichigo Ame", + romaji: "Ichigo Ame", + english: "Strawberry Candy", + native: "いちご飴", + }, + bannerImage: null, + 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", + color: "#e4c9a1", + }, + episodes: 1, + genres: ["Drama", "Psychological"], + tags: [], + season: null, + format: "MOVIE", + seasonYear: null, + averageScore: null, + nextAiringEpisode: null, + }, + { + id: 148622, + idMal: null, + status: "FINISHED", + title: { + userPreferred: "Ame yo Ame yo Fure", + romaji: "Ame yo Ame yo Fure", + english: null, + native: "雨よ雨よ降れ", + }, + bannerImage: + "https://s4.anilist.co/file/anilistcdn/media/anime/banner/148622-sWpb636NSlqM.jpg", + 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", + color: "#f1bb28", + }, + episodes: 1, + genres: [], + tags: [ + { + id: 265, + name: "Musical", + }, + ], + season: null, + format: "MUSIC", + seasonYear: null, + averageScore: null, + nextAiringEpisode: null, + }, + ], + }); + } + + return HttpResponse.json({ + code: 200, + message: "success", + pageInfo: { + total: 36, + perPage: 50, + currentPage: 1, + lastPage: 1, + hasNextPage: false, + }, + results: [ + { + id: 151807, + idMal: 52299, + status: "FINISHED", + title: { + userPreferred: "Ore dake Level Up na Ken", + romaji: "Ore dake Level Up na Ken", + english: "Solo Leveling", + native: "俺だけレベルアップな件", + }, + bannerImage: + "https://s4.anilist.co/file/anilistcdn/media/anime/banner/151807-37yfQA3ym8PA.jpg", + 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", + color: "#35bbf1", + }, + episodes: 12, + genres: ["Action", "Adventure", "Fantasy"], + tags: [ + { + id: 604, + name: "Dungeon", + }, + { + id: 82, + name: "Male Protagonist", + }, + { + id: 321, + name: "Urban Fantasy", + }, + { + id: 29, + name: "Magic", + }, + { + id: 66, + name: "Super Power", + }, + { + id: 1243, + name: "Necromancy", + }, + { + id: 326, + name: "Cultivation", + }, + { + id: 111, + name: "War", + }, + { + id: 104, + name: "Anti-Hero", + }, + { + id: 94, + name: "Gore", + }, + { + id: 636, + name: "Cosmic Horror", + }, + { + id: 85, + name: "Tragedy", + }, + { + id: 43, + name: "Swordplay", + }, + { + id: 56, + name: "Shounen", + }, + { + id: 146, + name: "Alternate Universe", + }, + { + id: 1068, + name: "Angels", + }, + { + id: 96, + name: "Time Manipulation", + }, + { + id: 93, + name: "Post-Apocalyptic", + }, + { + id: 217, + name: "Dystopian", + }, + { + id: 1310, + name: "Travel", + }, + { + id: 488, + name: "Age Regression", + }, + { + id: 1045, + name: "Heterosexual", + }, + { + id: 244, + name: "Isekai", + }, + { + id: 171, + name: "Bullying", + }, + { + id: 224, + name: "Dragons", + }, + { + id: 255, + name: "Ninja", + }, + ], + season: "WINTER", + format: "TV", + seasonYear: 2024, + averageScore: 83, + nextAiringEpisode: null, + }, + { + id: 2759, + idMal: 2759, + status: "FINISHED", + title: { + userPreferred: "Evangelion Shin Movie: Jo", + romaji: "Evangelion Shin Movie: Jo", + english: "Evangelion: 1.0 You Are (Not) Alone", + native: "ヱヴァンゲリヲン新劇場版:序", + }, + bannerImage: + "https://s4.anilist.co/file/anilistcdn/media/anime/banner/2759-EzK5WpFQz5ZT.jpg", + 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", + color: "#e4bba1", + }, + episodes: 1, + genres: ["Action", "Drama", "Mecha", "Psychological", "Sci-Fi"], + tags: [ + { + id: 93, + name: "Post-Apocalyptic", + }, + { + id: 82, + name: "Male Protagonist", + }, + { + id: 102, + name: "Coming of Age", + }, + { + id: 34, + name: "Military", + }, + { + id: 779, + name: "Kuudere", + }, + { + id: 217, + name: "Dystopian", + }, + { + id: 159, + name: "Super Robot", + }, + { + id: 276, + name: "Kaiju", + }, + { + id: 1228, + name: "Primarily Teen Cast", + }, + { + id: 191, + name: "Aliens", + }, + { + id: 90, + name: "CGI", + }, + { + id: 654, + name: "Denpa", + }, + { + id: 160, + name: "Real Robot", + }, + { + id: 100, + name: "Nudity", + }, + { + id: 46, + name: "School", + }, + ], + season: "SUMMER", + format: "MOVIE", + seasonYear: 2007, + averageScore: 77, + nextAiringEpisode: null, + }, + { + id: 139589, + idMal: 49909, + status: "FINISHED", + title: { + userPreferred: "Kotarou wa Hitorigurashi", + romaji: "Kotarou wa Hitorigurashi", + english: "Kotaro Lives Alone", + native: "コタローは1人暮らし", + }, + bannerImage: + "https://s4.anilist.co/file/anilistcdn/media/anime/banner/139589-IT7nRd5HGrGN.jpg", + 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", + color: "#1aaef1", + }, + episodes: 10, + genres: ["Comedy", "Drama", "Slice of Life"], + tags: [ + { + id: 1277, + name: "Found Family", + }, + { + id: 87, + name: "Family Life", + }, + { + id: 85, + name: "Tragedy", + }, + { + id: 193, + name: "Episodic", + }, + { + id: 109, + name: "Primarily Adult Cast", + }, + { + id: 102, + name: "Coming of Age", + }, + { + id: 82, + name: "Male Protagonist", + }, + { + id: 50, + name: "Seinen", + }, + { + id: 81, + name: "Iyashikei", + }, + { + id: 46, + name: "School", + }, + { + id: 1063, + name: "Cute Boys Doing Cute Things", + }, + { + id: 395, + name: "Delinquents", + }, + { + id: 694, + name: "Detective", + }, + ], + season: "WINTER", + format: "ONA", + seasonYear: 2022, + averageScore: 82, + nextAiringEpisode: null, + }, + { + id: 145815, + idMal: 51128, + status: "FINISHED", + title: { + userPreferred: + "Noumin Kanren no Skill Bakka Agetetara Naze ka Tsuyoku Natta.", + romaji: + "Noumin Kanren no Skill Bakka Agetetara Naze ka Tsuyoku Natta.", + english: + "I've Somehow Gotten Stronger When I Improved My Farm-Related Skills", + native: "農民関連のスキルばっか上げてたら何故か強くなった。", + }, + bannerImage: null, + 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", + color: "#50aef1", + }, + episodes: 12, + genres: ["Adventure", "Comedy", "Fantasy", "Slice of Life"], + tags: [ + { + id: 82, + name: "Male Protagonist", + }, + { + id: 90, + name: "CGI", + }, + { + id: 1052, + name: "Adoption", + }, + { + id: 66, + name: "Super Power", + }, + { + id: 15, + name: "Demons", + }, + { + id: 23, + name: "Female Harem", + }, + { + id: 224, + name: "Dragons", + }, + { + id: 909, + name: "Agriculture", + }, + { + id: 39, + name: "Parody", + }, + { + id: 240, + name: "Amnesia", + }, + { + id: 85, + name: "Tragedy", + }, + ], + season: "FALL", + format: "TV", + seasonYear: 2022, + averageScore: 57, + nextAiringEpisode: null, + }, + { + id: 176496, + idMal: 58567, + status: "NOT_YET_RELEASED", + title: { + userPreferred: + "Ore dake Level Up na Ken: Season 2 - Arise from the Shadow", + romaji: + "Ore dake Level Up na Ken: Season 2 - Arise from the Shadow", + english: "Solo Leveling Season 2 -Arise from the Shadow-", + native: "俺だけレベルアップな件 Season 2 -Arise from the Shadow-", + }, + bannerImage: null, + 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", + color: "#a1bbe4", + }, + episodes: null, + genres: ["Action", "Adventure", "Fantasy"], + tags: [ + { + id: 1243, + name: "Necromancy", + }, + { + id: 604, + name: "Dungeon", + }, + ], + season: null, + format: "TV", + seasonYear: null, + averageScore: null, + nextAiringEpisode: null, + }, + { + id: 1965, + idMal: 1965, + status: "FINISHED", + title: { + userPreferred: "sola", + romaji: "sola", + english: null, + native: "sola", + }, + bannerImage: + "https://s4.anilist.co/file/anilistcdn/media/anime/banner/1965-79LP1GTIN67M.jpg", + 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", + color: "#50aee4", + }, + episodes: 13, + genres: ["Drama", "Romance", "Slice of Life", "Supernatural"], + tags: [ + { + id: 365, + name: "Memory Manipulation", + }, + { + id: 321, + name: "Urban Fantasy", + }, + { + id: 139, + name: "Love Triangle", + }, + { + id: 66, + name: "Super Power", + }, + { + id: 85, + name: "Tragedy", + }, + { + id: 779, + name: "Kuudere", + }, + { + id: 1537, + name: "Coastal", + }, + { + id: 1045, + name: "Heterosexual", + }, + { + id: 50, + name: "Seinen", + }, + { + id: 233, + name: "Youkai", + }, + { + id: 240, + name: "Amnesia", + }, + ], + season: "SPRING", + format: "TV", + seasonYear: 2007, + averageScore: 67, + nextAiringEpisode: null, + }, + { + id: 118123, + idMal: 44042, + status: "RELEASING", + title: { + userPreferred: "Holo no Graffiti", + romaji: "Holo no Graffiti", + english: null, + native: "ホロのぐらふぃてぃ", + }, + bannerImage: + "https://s4.anilist.co/file/anilistcdn/media/anime/banner/118123-BvHyzWd850nZ.jpg", + 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", + color: "#e4ae5d", + }, + episodes: null, + genres: ["Action", "Adventure", "Comedy"], + tags: [ + { + id: 1047, + name: "VTuber", + }, + { + id: 86, + name: "Primarily Female Cast", + }, + { + id: 105, + name: "Ensemble Cast", + }, + { + id: 39, + name: "Parody", + }, + { + id: 92, + name: "Cute Girls Doing Cute Things", + }, + { + id: 115, + name: "Idol", + }, + { + id: 89, + name: "Full CGI", + }, + { + id: 83, + name: "Slapstick", + }, + { + id: 193, + name: "Episodic", + }, + { + id: 144, + name: "Meta", + }, + { + id: 281, + name: "Surreal Comedy", + }, + { + id: 308, + name: "Video Games", + }, + { + id: 665, + name: "Succubus", + }, + { + id: 113, + name: "Nekomimi", + }, + { + id: 456, + name: "Conspiracy", + }, + { + id: 76, + name: "Yuri", + }, + { + id: 254, + name: "Kemonomimi", + }, + { + id: 209, + name: "Dancing", + }, + { + id: 468, + name: "Shrine Maiden", + }, + { + id: 249, + name: "Maids", + }, + { + id: 931, + name: "Tomboy", + }, + { + id: 110, + name: "Band", + }, + { + id: 201, + name: "Pirates", + }, + { + id: 179, + name: "Witch", + }, + { + id: 653, + name: "Office Lady", + }, + { + id: 15, + name: "Demons", + }, + { + id: 694, + name: "Detective", + }, + { + id: 1105, + name: "Judo", + }, + { + id: 224, + name: "Dragons", + }, + { + id: 598, + name: "Elf", + }, + { + id: 74, + name: "Vampire", + }, + { + id: 101, + name: "Battle Royale", + }, + { + id: 220, + name: "Ghost", + }, + { + id: 212, + name: "Fishing", + }, + { + id: 1068, + name: "Angels", + }, + { + id: 160, + name: "Real Robot", + }, + { + id: 63, + name: "Space", + }, + { + id: 276, + name: "Kaiju", + }, + { + id: 154, + name: "Body Swapping", + }, + { + id: 1243, + name: "Necromancy", + }, + { + id: 191, + name: "Aliens", + }, + { + id: 10, + name: "Cars", + }, + ], + season: "SPRING", + format: "ONA", + seasonYear: 2019, + averageScore: 79, + nextAiringEpisode: { + airingAt: 1716109200, + timeUntilAiring: 277002, + episode: 261, + }, + }, + { + id: 2582, + idMal: 2582, + status: "FINISHED", + title: { + userPreferred: "Soukou Kihei Votoms", + romaji: "Soukou Kihei Votoms", + english: "Armored Trooper Votoms", + native: "装甲騎兵ボトムズ", + }, + bannerImage: + "https://s4.anilist.co/file/anilistcdn/media/anime/banner/2582-rlQUrWDtXDjf.jpg", + 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", + color: "#e47850", + }, + episodes: 52, + genres: [ + "Action", + "Drama", + "Mecha", + "Mystery", + "Romance", + "Sci-Fi", + ], + tags: [ + { + id: 34, + name: "Military", + }, + { + id: 111, + name: "War", + }, + { + id: 63, + name: "Space", + }, + { + id: 160, + name: "Real Robot", + }, + { + id: 217, + name: "Dystopian", + }, + { + id: 104, + name: "Anti-Hero", + }, + { + id: 252, + name: "Revenge", + }, + { + id: 327, + name: "Noir", + }, + { + id: 162, + name: "Space Opera", + }, + { + id: 157, + name: "Guns", + }, + { + id: 253, + name: "Gods", + }, + { + id: 1121, + name: "Torture", + }, + { + id: 108, + name: "Cyberpunk", + }, + ], + season: "SPRING", + format: "TV", + seasonYear: 1983, + averageScore: 74, + nextAiringEpisode: null, + }, + { + id: 116384, + idMal: 40597, + status: "FINISHED", + title: { + userPreferred: "Sol Levante", + romaji: "Sol Levante", + english: "Sol Levante", + native: "Sol Levante", + }, + bannerImage: + "https://s4.anilist.co/file/anilistcdn/media/anime/banner/116384-gLKcXdbpQCet.jpg", + 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", + color: "#e40d0d", + }, + episodes: 1, + genres: ["Fantasy"], + tags: [ + { + id: 89, + name: "Full CGI", + }, + { + id: 341, + name: "No Dialogue", + }, + { + id: 29, + name: "Magic", + }, + { + id: 98, + name: "Female Protagonist", + }, + { + id: 156, + name: "Achronological Order", + }, + ], + season: "SPRING", + format: "ONA", + seasonYear: 2020, + averageScore: 46, + nextAiringEpisode: null, + }, + { + id: 104073, + idMal: 38251, + status: "FINISHED", + title: { + userPreferred: "Sono Toki, Kanojo wa.", + romaji: "Sono Toki, Kanojo wa.", + english: null, + native: "その時、カノジョは。", + }, + bannerImage: + "https://s4.anilist.co/file/anilistcdn/media/anime/banner/n104073-hSR50Nfk41Yy.jpg", + 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", + color: null, + }, + episodes: 12, + genres: ["Drama", "Romance", "Slice of Life"], + tags: [ + { + id: 98, + name: "Female Protagonist", + }, + { + id: 1045, + name: "Heterosexual", + }, + { + id: 193, + name: "Episodic", + }, + { + id: 471, + name: "Anthology", + }, + ], + season: "FALL", + format: "TV_SHORT", + seasonYear: 2018, + averageScore: 50, + nextAiringEpisode: null, + }, + { + id: 15313, + idMal: 15313, + status: "FINISHED", + title: { + userPreferred: "Wooser no Sono Higurashi", + romaji: "Wooser no Sono Higurashi", + english: "Wooser's Hand-to-Mouth Life", + native: "うーさーのその日暮らし", + }, + bannerImage: null, + 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", + color: "#e4ae5d", + }, + episodes: 12, + genres: ["Comedy", "Fantasy"], + tags: [ + { + id: 324, + name: "Chibi", + }, + { + id: 39, + name: "Parody", + }, + ], + season: "FALL", + format: "TV_SHORT", + seasonYear: 2012, + averageScore: 57, + nextAiringEpisode: null, + }, + { + id: 8068, + idMal: 8068, + status: "FINISHED", + title: { + userPreferred: "Kuroshitsuji Picture Drama", + romaji: "Kuroshitsuji Picture Drama", + english: null, + native: "黒執事 ピクチャードラマ", + }, + bannerImage: 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", + color: "#86350d", + }, + episodes: 1, + genres: ["Supernatural"], + tags: [ + { + id: 812, + name: "Butler", + }, + { + id: 15, + name: "Demons", + }, + { + id: 779, + name: "Kuudere", + }, + ], + season: "WINTER", + format: "SPECIAL", + seasonYear: 2010, + averageScore: 61, + nextAiringEpisode: null, + }, + { + id: 3174, + idMal: 3174, + status: "FINISHED", + title: { + userPreferred: "sola Specials", + romaji: "sola Specials", + english: null, + native: "sola", + }, + bannerImage: 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", + color: "#1a4386", + }, + episodes: 2, + genres: ["Comedy", "Romance", "Slice of Life", "Supernatural"], + tags: [ + { + id: 779, + name: "Kuudere", + }, + { + id: 233, + name: "Youkai", + }, + ], + season: "FALL", + format: "SPECIAL", + seasonYear: 2007, + averageScore: 64, + nextAiringEpisode: null, + }, + { + id: 1443, + idMal: 1443, + status: "FINISHED", + title: { + userPreferred: "SOL BIANCA", + romaji: "SOL BIANCA", + english: null, + native: "SOL BIANCA", + }, + bannerImage: 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", + color: "#e4a15d", + }, + episodes: 2, + genres: ["Adventure", "Sci-Fi"], + tags: [ + { + id: 201, + name: "Pirates", + }, + { + id: 63, + name: "Space", + }, + { + id: 157, + name: "Guns", + }, + { + id: 86, + name: "Primarily Female Cast", + }, + { + id: 98, + name: "Female Protagonist", + }, + { + id: 109, + name: "Primarily Adult Cast", + }, + ], + season: "SPRING", + format: "OVA", + seasonYear: 1990, + averageScore: 55, + nextAiringEpisode: null, + }, + { + id: 153431, + idMal: 52601, + status: "FINISHED", + title: { + userPreferred: "Onna no Sono no Hoshi", + romaji: "Onna no Sono no Hoshi", + english: null, + native: "女の園の星", + }, + bannerImage: 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", + color: "#f1d6bb", + }, + episodes: 1, + genres: ["Comedy", "Slice of Life"], + tags: [ + { + id: 82, + name: "Male Protagonist", + }, + { + id: 46, + name: "School", + }, + { + id: 165, + name: "Teacher", + }, + { + id: 27, + name: "Josei", + }, + ], + season: "FALL", + format: "OVA", + seasonYear: 2022, + averageScore: 54, + nextAiringEpisode: null, + }, + { + id: 1444, + idMal: 1444, + status: "FINISHED", + title: { + userPreferred: "Sol Bianca: Taiyou no Fune", + romaji: "Sol Bianca: Taiyou no Fune", + english: "Sol Bianca: The Legacy", + native: "太陽の船 ソルビアンカ", + }, + bannerImage: null, + 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", + color: "#fe8628", + }, + episodes: 6, + genres: ["Adventure", "Mecha", "Sci-Fi"], + tags: [ + { + id: 63, + name: "Space", + }, + { + id: 201, + name: "Pirates", + }, + { + id: 517, + name: "Artificial Intelligence", + }, + { + id: 161, + name: "Bar", + }, + { + id: 466, + name: "Lost Civilization", + }, + { + id: 98, + name: "Female Protagonist", + }, + { + id: 86, + name: "Primarily Female Cast", + }, + { + id: 157, + name: "Guns", + }, + { + id: 364, + name: "Augmented Reality", + }, + { + id: 100, + name: "Nudity", + }, + { + id: 85, + name: "Tragedy", + }, + ], + season: "FALL", + format: "OVA", + seasonYear: 1999, + averageScore: 55, + nextAiringEpisode: null, + }, + { + id: 4138, + idMal: 4138, + status: "FINISHED", + title: { + userPreferred: "Chiisana Pengin: Lolo no Bouken", + romaji: "Chiisana Pengin: Lolo no Bouken", + english: "The Adventures of Scamper the Penguin", + native: "小さなペンギンロロの冒険", + }, + bannerImage: null, + 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", + color: "#e45d86", + }, + episodes: 3, + genres: ["Adventure", "Drama", "Fantasy", "Slice of Life"], + tags: [ + { + id: 1616, + name: "Snowscape", + }, + { + id: 433, + name: "Animals", + }, + { + id: 82, + name: "Male Protagonist", + }, + { + id: 28, + name: "Kids", + }, + ], + season: "WINTER", + format: "OVA", + seasonYear: 1986, + averageScore: 61, + nextAiringEpisode: null, + }, + { + id: 164192, + idMal: 55370, + status: "FINISHED", + title: { + userPreferred: "Planetarium", + romaji: "Planetarium", + english: "Planetarium", + native: "プラネタリウム", + }, + bannerImage: + "https://s4.anilist.co/file/anilistcdn/media/anime/banner/164192-gW7ML85JgAsJ.jpg", + 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", + color: "#3578ae", + }, + episodes: 1, + genres: [], + tags: [ + { + id: 1047, + name: "VTuber", + }, + { + id: 115, + name: "Idol", + }, + ], + season: null, + format: "MUSIC", + seasonYear: null, + averageScore: 69, + nextAiringEpisode: null, + }, + { + id: 5838, + idMal: 5838, + status: "FINISHED", + title: { + userPreferred: "Furudera no Obake-soudou", + romaji: "Furudera no Obake-soudou", + english: null, + native: "古寺のおばけ騒動", + }, + bannerImage: 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", + color: null, + }, + episodes: 1, + genres: ["Action", "Adventure"], + tags: [ + { + id: 233, + name: "Youkai", + }, + { + id: 710, + name: "Achromatic", + }, + ], + season: null, + format: "MOVIE", + seasonYear: null, + averageScore: 42, + nextAiringEpisode: null, + }, + { + id: 162882, + idMal: 55368, + status: "FINISHED", + title: { + userPreferred: "P.E.T.", + romaji: "P.E.T.", + english: "P.E.T.", + native: "P.E.T.", + }, + bannerImage: null, + 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", + color: "#862843", + }, + episodes: 1, + genres: ["Psychological"], + tags: [ + { + id: 1047, + name: "VTuber", + }, + ], + season: null, + format: "MUSIC", + seasonYear: null, + averageScore: 63, + nextAiringEpisode: null, + }, + { + id: 102710, + idMal: 29952, + status: "FINISHED", + title: { + userPreferred: "Kairaku no Sono", + romaji: "Kairaku no Sono", + english: "The Garden of Pleasure", + native: "快楽の園", + }, + bannerImage: null, + 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", + color: null, + }, + episodes: 1, + genres: ["Music"], + tags: [ + { + id: 710, + name: "Achromatic", + }, + { + id: 341, + name: "No Dialogue", + }, + { + id: 265, + name: "Musical", + }, + ], + season: null, + format: "MOVIE", + seasonYear: null, + averageScore: 38, + nextAiringEpisode: null, + }, + { + id: 162881, + idMal: 55367, + status: "FINISHED", + title: { + userPreferred: "Mosh Race", + romaji: "Mosh Race", + english: "Mosh Race", + native: "モッシュレース", + }, + bannerImage: null, + 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", + color: "#e45d6b", + }, + episodes: 1, + genres: [], + tags: [ + { + id: 1047, + name: "VTuber", + }, + ], + season: null, + format: "MUSIC", + seasonYear: null, + averageScore: 57, + nextAiringEpisode: null, + }, + { + id: 5935, + idMal: 5935, + status: "FINISHED", + title: { + userPreferred: "Marco Polo no Boken", + romaji: "Marco Polo no Boken", + english: "Marco Polo's Adventures", + native: "マルコ・ポーロの冒険", + }, + bannerImage: null, + 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", + color: "#f1ae5d", + }, + episodes: 43, + genres: ["Adventure"], + tags: [ + { + id: 25, + name: "Historical", + }, + ], + season: "SPRING", + format: "TV", + seasonYear: 1979, + averageScore: null, + nextAiringEpisode: null, + }, + { + id: 103449, + idMal: 31450, + status: "FINISHED", + title: { + userPreferred: "SOL", + romaji: "SOL", + english: null, + native: "SOL", + }, + bannerImage: 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", + color: "#f1e45d", + }, + episodes: 1, + genres: [], + tags: [], + season: null, + format: "MUSIC", + seasonYear: null, + averageScore: 38, + nextAiringEpisode: null, + }, + { + id: 12993, + idMal: 12993, + status: "FINISHED", + title: { + userPreferred: "Sono Mukou no Mukougawa", + romaji: "Sono Mukou no Mukougawa", + english: null, + native: "その向こうの向こう側", + }, + bannerImage: 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", + color: "#e4c978", + }, + episodes: 1, + genres: [], + tags: [], + season: null, + format: "MUSIC", + seasonYear: null, + averageScore: 46, + nextAiringEpisode: null, + }, + { + id: 20459, + idMal: 20755, + status: "FINISHED", + title: { + userPreferred: "Ganbare! Lulu Lolo", + romaji: "Ganbare! Lulu Lolo", + english: null, + native: "がんばれ! ルルロロ", + }, + bannerImage: 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", + color: "#f1bb1a", + }, + episodes: 26, + genres: ["Slice of Life"], + tags: [ + { + id: 28, + name: "Kids", + }, + ], + season: "WINTER", + format: "TV_SHORT", + seasonYear: 2013, + averageScore: null, + nextAiringEpisode: null, + }, + { + id: 137760, + idMal: 41764, + status: "FINISHED", + title: { + userPreferred: "Soko ni wa Mata Meikyuu", + romaji: "Soko ni wa Mata Meikyuu", + english: null, + native: "そこにはまた迷宮", + }, + bannerImage: 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", + color: "#aee4c9", + }, + episodes: 1, + genres: [], + tags: [], + season: null, + format: "MUSIC", + seasonYear: null, + averageScore: 51, + nextAiringEpisode: null, + }, + { + id: 7473, + idMal: 7473, + status: "FINISHED", + title: { + userPreferred: "Rennyo to Sono Haha", + romaji: "Rennyo to Sono Haha", + english: "Rennyo and His Mother", + native: "蓮如とその母", + }, + bannerImage: null, + 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", + color: null, + }, + episodes: 1, + genres: [], + tags: [ + { + id: 25, + name: "Historical", + }, + ], + season: "FALL", + format: "MOVIE", + seasonYear: 1981, + averageScore: null, + nextAiringEpisode: null, + }, + { + id: 21418, + idMal: 31753, + status: "FINISHED", + title: { + userPreferred: "Ganbare! Lulu Lolo 3rd Season", + romaji: "Ganbare! Lulu Lolo 3rd Season", + english: null, + native: "がんばれ!ルルロロ 第3シリーズ", + }, + bannerImage: 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", + color: "#e4c943", + }, + episodes: 10, + genres: [], + tags: [ + { + id: 28, + name: "Kids", + }, + ], + season: "WINTER", + format: "TV_SHORT", + seasonYear: 2016, + averageScore: null, + nextAiringEpisode: null, + }, + { + id: 103517, + idMal: 32758, + status: "FINISHED", + title: { + userPreferred: "Toute wa Sono Kotae", + romaji: "Toute wa Sono Kotae", + english: null, + native: "問うてはその応え", + }, + bannerImage: 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", + color: null, + }, + episodes: 1, + genres: ["Supernatural"], + tags: [], + season: null, + format: "MUSIC", + seasonYear: null, + averageScore: 45, + nextAiringEpisode: null, + }, + { + id: 113572, + idMal: 52438, + status: "FINISHED", + title: { + userPreferred: "Sono Saki no Taniji", + romaji: "Sono Saki no Taniji", + english: "Journey to the beyond", + native: "その先の旅路", + }, + bannerImage: null, + 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", + color: null, + }, + episodes: 1, + genres: ["Mystery"], + tags: [ + { + id: 779, + name: "Kuudere", + }, + ], + season: null, + format: "MOVIE", + seasonYear: null, + averageScore: 53, + nextAiringEpisode: null, + }, + { + id: 20864, + idMal: 26163, + status: "FINISHED", + title: { + userPreferred: "Ganbare! Lulu Lolo 2nd Season", + romaji: "Ganbare! Lulu Lolo 2nd Season", + english: null, + native: "がんばれ!ルルロロ 第2シリーズ", + }, + bannerImage: 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", + color: "#f1bb1a", + }, + episodes: 26, + genres: [], + tags: [ + { + id: 28, + name: "Kids", + }, + ], + season: "FALL", + format: "TV_SHORT", + seasonYear: 2014, + averageScore: null, + nextAiringEpisode: null, + }, + { + id: 15129, + idMal: 15129, + status: "FINISHED", + title: { + userPreferred: "Tanpen Animation Junpei Fujita", + romaji: "Tanpen Animation Junpei Fujita", + english: "Short Animations of Junpei Fujita", + native: "短編アニメーション Junpei Fujita", + }, + bannerImage: null, + 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", + color: "#6bd678", + }, + episodes: 3, + genres: ["Music"], + tags: [], + season: "SUMMER", + format: "SPECIAL", + seasonYear: 2009, + averageScore: null, + nextAiringEpisode: null, + }, + { + id: 106557, + idMal: null, + status: "FINISHED", + title: { + userPreferred: "Sono Ie no Namae", + romaji: "Sono Ie no Namae", + english: "A Place to Name", + native: "その家の名前", + }, + bannerImage: + "https://s4.anilist.co/file/anilistcdn/media/anime/banner/106557-vqnpo7jHJXHC.jpg", + 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", + color: null, + }, + episodes: 1, + genres: ["Psychological"], + tags: [], + season: null, + format: "MOVIE", + seasonYear: null, + averageScore: null, + nextAiringEpisode: null, + }, + { + id: 118133, + idMal: 53658, + status: "FINISHED", + title: { + userPreferred: "Guzu no Soko", + romaji: "Guzu no Soko", + english: "In Inertia", + native: "愚図の底", + }, + bannerImage: null, + 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", + color: "#e4f1bb", + }, + episodes: 1, + genres: ["Psychological"], + tags: [], + season: null, + format: "MOVIE", + seasonYear: null, + averageScore: null, + nextAiringEpisode: null, + }, + { + id: 169686, + idMal: null, + status: "FINISHED", + title: { + userPreferred: "Soto ni Denai hi", + romaji: "Soto ni Denai hi", + english: "Indoor Days", + native: "外に出ない日", + }, + bannerImage: null, + 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", + color: null, + }, + episodes: 1, + genres: ["Psychological"], + tags: [ + { + id: 144, + name: "Meta", + }, + ], + season: null, + format: "MOVIE", + seasonYear: null, + averageScore: null, + nextAiringEpisode: null, + }, + ], + }); + }, + ); +} diff --git a/src/mocks/anilist/search.ts b/src/mocks/anilist/search.ts new file mode 100644 index 0000000..979aaee --- /dev/null +++ b/src/mocks/anilist/search.ts @@ -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, + }, + }, + }, + }); + }); +} diff --git a/src/mocks/handlers.ts b/src/mocks/handlers.ts index 0a8cbfb..7dad273 100644 --- a/src/mocks/handlers.ts +++ b/src/mocks/handlers.ts @@ -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(), +]; diff --git a/src/types/schema.ts b/src/types/schema.ts index 6b60d11..8075947 100644 --- a/src/types/schema.ts +++ b/src/types/schema.ts @@ -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), }); diff --git a/src/types/title/index.ts b/src/types/title/index.ts index 096b0f6..f57f5e4 100644 --- a/src/types/title/index.ts +++ b/src/types/title/index.ts @@ -56,5 +56,5 @@ export const Title = z.object({ }), ), idMal: z.number().nullable(), - id: z.number(), + id: z.number().openapi({ type: "integer", format: "int64" }), });