import { findBestMatchingTitle } from "~/libs/findBestMatchingTitle"; import { Episode, type EpisodesResponse } from "~/types/episode"; export async function getEpisodesFromAniwatch( aniListId: number, ): Promise { try { const animeTitle = await import("~/libs/anilist/getTitle") .then(({ fetchTitleFromAnilist }) => fetchTitleFromAnilist(aniListId, undefined), ) .then((title) => ({ english: title?.title?.english, userPreferred: title?.title?.userPreferred, })); if (!animeTitle.english && !animeTitle.userPreferred) { return null; } const aniwatchId = await getAniwatchId(animeTitle); if (!aniwatchId) { return null; } const episodes: Episode[] | null = await fetch( `https://aniwatch.up.railway.app/anime/episodes/${aniwatchId}`, ) .then((res) => res.json()) .then(({ totalEpisodes, episodes }) => { if (totalEpisodes === 0) { console.error( `Error trying to load episodes from aniwatch; aniListId: ${aniListId}, totalEpisodes: ${totalEpisodes}`, ); return null; } return episodes.map(({ episodeId, number, title }) => ({ id: episodeId, number, title, updatedAt: 0, })); }); if (!episodes || episodes.length === 0) { return null; } return { providerId: "aniwatch", episodes }; } catch (error) { console.error( new Error( `Error trying to load episodes from aniwatch; aniListId: ${aniListId}`, { cause: error }, ), ); } return null; } function getAniwatchId( animeTitle: Partial<{ english: string; userPreferred: string }>, ): Promise { return fetch( `https://aniwatch.up.railway.app/anime/search?q=${encodeURIComponent(animeTitle.english ?? animeTitle.userPreferred!)}`, ) .then((res) => res.json()) .then(({ animes }) => { const bestMatchingTitle = findBestMatchingTitle( animeTitle, animes.map((anime) => ({ english: anime.name, userPreferred: anime.jname, })), ); return animes.find( (anime) => anime.name === bestMatchingTitle || anime.jname === bestMatchingTitle, )?.id; }); } export interface AniwatchEpisodesResponse { totalEpisodes: number; episodes: AniwatchEpisode[]; } export interface AniwatchEpisode { title: string; episodeId: string; number: number; isFiller: boolean; } export interface AniwatchSearchResponse { animes: Anime[]; currentPage: number; hasNextPage: boolean; totalPages: number; } interface Anime { id: string; name: string; jname: string; poster: string; duration: string; type: Type; rating: null | string; episodes: Episodes; } interface Episodes { sub: number | null; dub: number | null; } enum Type { Movie = "Movie", Ona = "ONA", Ova = "OVA", Special = "Special", Tv = "TV", }