feat: add Consumet as source for /episodes
This commit is contained in:
@@ -4,6 +4,8 @@
|
||||
"deploy": "wrangler deploy --minify src/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@consumet/extensions": "github:consumet/consumet.ts#2bcd9287dc1471ed081bc23333e7629779924e0e",
|
||||
"@haverstack/axios-fetch-adapter": "^0.12.0",
|
||||
"@hono/swagger-ui": "^0.2.2",
|
||||
"@hono/zod-openapi": "^0.12.0",
|
||||
"gql.tada": "^1.7.5",
|
||||
|
||||
@@ -16,7 +16,7 @@ export async function getEpisodesFromAnify(
|
||||
response = await Promise.race([
|
||||
fetch(`https://api.anify.tv/episodes/${aniListId}`, {
|
||||
signal: abortController.signal,
|
||||
}).then((res) => res.json() as Promise<AnifyEpisodesResponse[]>),
|
||||
}).then((res) => res.json<AnifyEpisodesResponse[]>()),
|
||||
// set a limit of 30 seconds
|
||||
new Promise((resolve) => setTimeout(resolve, 30 * 1000)).then(() => {
|
||||
abortController.abort("Loading episodes from Anify timed out");
|
||||
|
||||
45
src/controllers/episodes/consumet.ts
Normal file
45
src/controllers/episodes/consumet.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { ANIME, META } from "@consumet/extensions";
|
||||
import fetchAdapter from "@haverstack/axios-fetch-adapter";
|
||||
|
||||
import { Episode, EpisodesResponse } from "./episode";
|
||||
|
||||
export async function getEpisodesFromConsumet(
|
||||
aniListId: number,
|
||||
): Promise<EpisodesResponse | null> {
|
||||
const gogoAnime = new ANIME.Gogoanime(undefined, undefined, fetchAdapter);
|
||||
const aniList = new META.Anilist(gogoAnime, undefined, fetchAdapter);
|
||||
|
||||
try {
|
||||
const episodes = await aniList
|
||||
.fetchEpisodesListById(aniListId.toString())
|
||||
.then((episodes) =>
|
||||
episodes.map(
|
||||
({ id, number, title, image: img, description }): Episode => ({
|
||||
id,
|
||||
number,
|
||||
title,
|
||||
img,
|
||||
description,
|
||||
rating: undefined,
|
||||
updatedAt: 0,
|
||||
}),
|
||||
),
|
||||
);
|
||||
if (!episodes || episodes.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { providerId: "consumet", episodes };
|
||||
} catch (error: any) {
|
||||
if (!error.message.includes("failed with status code")) {
|
||||
console.error(
|
||||
new Error(
|
||||
`Error trying to load episodes from consumet; aniListId: ${aniListId}`,
|
||||
{ cause: error },
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -2,8 +2,10 @@ import { describe, expect, it } from "bun:test";
|
||||
|
||||
import app from "~/index";
|
||||
import { server } from "~/mocks";
|
||||
import { mockConsumet } from "~/mocks/consumet";
|
||||
|
||||
server.listen();
|
||||
mockConsumet();
|
||||
|
||||
describe('requests the "/episodes" route', () => {
|
||||
it("with list of episodes from Anify", async () => {
|
||||
@@ -148,7 +150,7 @@ describe('requests the "/episodes" route', () => {
|
||||
|
||||
it("Anify is disabled, returns no episode list from Anify", async () => {
|
||||
const response = await app.request(
|
||||
"/episodes/1",
|
||||
"/episodes/2",
|
||||
{},
|
||||
{
|
||||
ENABLE_ANIFY: "false",
|
||||
@@ -159,8 +161,33 @@ describe('requests the "/episodes" route', () => {
|
||||
expect(response.status).toBe(404);
|
||||
});
|
||||
|
||||
it("with an unknown title from all sources", async () => {
|
||||
const response = await app.request("/title?id=-1");
|
||||
it("with list of episodes from Consumet", async () => {
|
||||
const response = await app.request(
|
||||
"/episodes/3",
|
||||
{},
|
||||
{
|
||||
ENABLE_ANIFY: "true",
|
||||
},
|
||||
);
|
||||
|
||||
expect(response.json()).resolves.toEqual({
|
||||
success: true,
|
||||
result: {
|
||||
providerId: "consumet",
|
||||
episodes: [
|
||||
{
|
||||
id: "consumet-1",
|
||||
number: 1,
|
||||
title: "Consumet 1",
|
||||
updatedAt: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("with no episodes from all sources", async () => {
|
||||
const response = await app.request("/episodes/-1");
|
||||
|
||||
expect(response.json()).resolves.toEqual({ success: false });
|
||||
expect(response.status).toBe(404);
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from "~/types/schema";
|
||||
|
||||
import { getEpisodesFromAnify } from "./anify";
|
||||
import { getEpisodesFromConsumet } from "./consumet";
|
||||
import { EpisodesResponse } from "./episode";
|
||||
|
||||
const EpisodesResponseSchema = SuccessResponseSchema(EpisodesResponse);
|
||||
@@ -48,6 +49,7 @@ app.openapi(route, async (c) => {
|
||||
|
||||
const episodes = await fetchFromMultipleSources([
|
||||
() => getEpisodesFromAnify(JSON.parse(c.env.ENABLE_ANIFY), aniListId),
|
||||
() => getEpisodesFromConsumet(aniListId),
|
||||
]);
|
||||
|
||||
if (!episodes) {
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { HttpResponse, http } from "msw";
|
||||
|
||||
export function getAnifyEpisodes() {
|
||||
return http.get("https://api.anify.tv/episodes/:aniListId", () => {
|
||||
return http.get("https://api.anify.tv/episodes/:aniListId", ({ params }) => {
|
||||
const aniListId = Number(params["aniListId"]);
|
||||
if (aniListId === 3 || aniListId < 0) {
|
||||
return HttpResponse.json([]);
|
||||
}
|
||||
|
||||
return HttpResponse.json([
|
||||
{
|
||||
providerId: "zoro",
|
||||
|
||||
31
src/mocks/consumet.ts
Normal file
31
src/mocks/consumet.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { IAnimeEpisode } from "@consumet/extensions";
|
||||
|
||||
import { mock } from "bun:test";
|
||||
|
||||
export function mockConsumet() {
|
||||
mock.module("@consumet/extensions", () => {
|
||||
class Gogoanime {}
|
||||
|
||||
class Anilist {
|
||||
fetchEpisodesListById(
|
||||
id: string,
|
||||
dub?: boolean | undefined,
|
||||
fetchFiller?: boolean | undefined,
|
||||
): Promise<IAnimeEpisode[]> {
|
||||
if (id === "3") {
|
||||
return Promise.resolve([
|
||||
{
|
||||
id: "consumet-1",
|
||||
number: 1,
|
||||
title: "Consumet 1",
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
}
|
||||
|
||||
return { ANIME: { Gogoanime }, META: { Anilist } };
|
||||
});
|
||||
}
|
||||
10
src/types/anilist-graphql.d.ts
vendored
10
src/types/anilist-graphql.d.ts
vendored
@@ -1,3 +1,5 @@
|
||||
import * as gqlTada from "gql.tada";
|
||||
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
|
||||
@@ -203,10 +205,8 @@ export type introspection = {
|
||||
};
|
||||
};
|
||||
|
||||
import * as gqlTada from 'gql.tada';
|
||||
|
||||
declare module 'gql.tada' {
|
||||
declare module "gql.tada" {
|
||||
interface setupSchema {
|
||||
introspection: introspection
|
||||
introspection: introspection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user