feat: add Consumet as provider for stream URL

Summary:

Test Plan:
This commit is contained in:
2024-05-30 09:47:07 -04:00
parent 40daf70209
commit 7e3c818828
7 changed files with 137 additions and 21 deletions

View File

@@ -0,0 +1,36 @@
import { aniList } from "~/consumet";
import { sortByProperty } from "~/libs/sortByProperty";
import { qualityPriority, subtitlesPriority } from "./priorities";
import type { FetchUrlResponse } from "./responseType";
export async function getSourcesFromConsumet(
watchId: string,
): Promise<FetchUrlResponse | null> {
try {
const { sources, subtitles, intro, outro } =
await aniList.fetchEpisodeSources(watchId);
if (sources.length === 0) {
return null;
}
const source = sources.sort(sortByProperty(qualityPriority, "quality"))[0]
?.url;
subtitles?.sort(sortByProperty(subtitlesPriority, "lang"));
return {
source,
subtitles: subtitles ?? [],
audio: [],
intro: intro ? [intro.start, intro.end] : undefined,
outro: outro ? [outro.start, outro.end] : undefined,
};
} catch (error) {
if (error.message === "Episode not found.") {
return null;
}
throw error;
}
}

View File

@@ -50,4 +50,44 @@ describe('requests the "/episodes/:id/url" route', () => {
expect(response.json()).resolves.toEqual({ success: false });
expect(response.status).toBe(404);
});
it("with sources from Consumet", async () => {
const response = await app.request(
"/episodes/1/url",
{
method: "POST",
body: JSON.stringify({
provider: "consumet",
id: "/ore-dake-level-up-na-ken-episode-2",
}),
headers: { "Content-Type": "application/json" },
},
{
ENABLE_ANIFY: "true",
},
);
expect(response.json()).resolves.toEqual({
success: true,
result: {
source: "https://consumet.com",
subtitles: [],
audio: [],
},
});
});
it("with no URL from Consumet source", async () => {
const response = await app.request("/episodes/-1/url", {
method: "POST",
body: JSON.stringify({
provider: "consumet",
id: "unknown",
}),
headers: { "Content-Type": "application/json" },
});
expect(response.json()).resolves.toEqual({ success: false });
expect(response.status).toBe(404);
});
});

View File

@@ -1,5 +1,6 @@
import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi";
import { readEnvVariable } from "~/libs/readEnvVariable";
import type { Env } from "~/types/env";
import {
ErrorResponse,
@@ -7,7 +8,6 @@ import {
SuccessResponseSchema,
} from "~/types/schema";
import { getSourcesFromAnify } from "./anify";
import { FetchUrlResponse as FetchUrlResponseSchema } from "./responseType";
const FetchUrlRequest = z.object({ id: z.string(), provider: z.string() });
@@ -38,6 +38,14 @@ const route = createRoute({
},
description: "Returns a stream URL",
},
400: {
content: {
"application/json": {
schema: ErrorResponseSchema,
},
},
description: "Unknown provider",
},
404: {
content: {
"application/json": {
@@ -63,9 +71,32 @@ app.openapi(route, async (c) => {
const aniListId = Number(c.req.param("aniListId"));
const { provider, id } = await c.req.json<typeof FetchUrlRequest._type>();
const isAnifyEnabled = readEnvVariable(c.env, "ENABLE_ANIFY");
if (provider === "consumet" || !isAnifyEnabled) {
try {
const result = await import("./consumet").then(
({ getSourcesFromConsumet }) => getSourcesFromConsumet(id),
);
if (!result) {
return c.json({ success: false }, { status: 404 });
}
return c.json({
success: true,
result,
});
} catch (e) {
console.error("Failed to fetch download URL from Consumet", e);
return c.json(ErrorResponse, { status: 500 });
}
}
if (provider === "anify") {
try {
const result = await getSourcesFromAnify(provider, id, aniListId);
const result = await import("./anify").then(({ getSourcesFromAnify }) =>
getSourcesFromAnify(provider, id, aniListId),
);
if (!result) {
return c.json({ success: false }, { status: 404 });
}
@@ -80,6 +111,8 @@ app.openapi(route, async (c) => {
return c.json(ErrorResponse, { status: 500 });
}
}
return c.json(ErrorResponse, { status: 400 });
});
export default app;