feat: support legacy routes
Summary: Test Plan:
This commit is contained in:
117
src/controllers/episodes/downloadLegacy.ts
Normal file
117
src/controllers/episodes/downloadLegacy.ts
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi";
|
||||||
|
import { env } from "hono/adapter";
|
||||||
|
|
||||||
|
import { Env } from "~/types/env";
|
||||||
|
import {
|
||||||
|
AniListIdSchema,
|
||||||
|
ErrorResponse,
|
||||||
|
ErrorResponseSchema,
|
||||||
|
SkippableSchema,
|
||||||
|
SuccessResponseSchema,
|
||||||
|
} from "~/types/schema";
|
||||||
|
|
||||||
|
import { getSourcesFromAmvstrm } from "./getEpisodeUrl/amvstrm";
|
||||||
|
import { getSourcesFromAnify } from "./getEpisodeUrl/anify";
|
||||||
|
import { getSourcesFromConsumet } from "./getEpisodeUrl/consumet";
|
||||||
|
|
||||||
|
const DownloadRequest = z.object({
|
||||||
|
provider: z.string(),
|
||||||
|
watchId: z.string(),
|
||||||
|
aniListId: AniListIdSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
const DownloadResponse = SuccessResponseSchema(
|
||||||
|
z.object({
|
||||||
|
source: z.string(),
|
||||||
|
subtitles: z.array(z.object({ url: z.string(), lang: z.string() })),
|
||||||
|
audio: z.array(z.object({ url: z.string(), lang: z.string() })),
|
||||||
|
intro: SkippableSchema,
|
||||||
|
outro: SkippableSchema,
|
||||||
|
headers: z.record(z.string()).optional(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const route = createRoute({
|
||||||
|
tags: ["aniplay", "episodes"],
|
||||||
|
summary: "Fetch stream URL (usually an .m3u8 stream)",
|
||||||
|
method: "post",
|
||||||
|
path: "/",
|
||||||
|
request: {
|
||||||
|
body: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: DownloadRequest,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Returns a stream URL",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: DownloadResponse,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
description: "Invalid request body",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new OpenAPIHono();
|
||||||
|
|
||||||
|
app.openapi(route, async (c) => {
|
||||||
|
const { provider, watchId, aniListId } =
|
||||||
|
await c.req.json<typeof DownloadRequest._type>();
|
||||||
|
|
||||||
|
if (provider === "amvstrm") {
|
||||||
|
try {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
result: await getSourcesFromAmvstrm(watchId),
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to fetch download URL from Amvstrm", e);
|
||||||
|
|
||||||
|
return c.json(ErrorResponse, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (provider === "consumet" || !env<Env>(c).ENABLE_ANIFY) {
|
||||||
|
try {
|
||||||
|
return c.json({
|
||||||
|
success: true,
|
||||||
|
result: await getSourcesFromConsumet(watchId),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to fetch download URL from Consumet", e);
|
||||||
|
|
||||||
|
return c.json(ErrorResponse, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// provider is not consumet
|
||||||
|
if (!aniListId) {
|
||||||
|
return c.json(ErrorResponse, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return c.json({
|
||||||
|
success: true,
|
||||||
|
result: await getSourcesFromAnify(provider, watchId, aniListId),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to fetch download URL from Anify", e);
|
||||||
|
|
||||||
|
return c.json(ErrorResponse, { status: 500 });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app;
|
||||||
77
src/controllers/episodes/getEpisodesLegacy.ts
Normal file
77
src/controllers/episodes/getEpisodesLegacy.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi";
|
||||||
|
|
||||||
|
import { fetchFromMultipleSources } from "~/libs/fetchFromMultipleSources";
|
||||||
|
import { readEnvVariable } from "~/libs/readEnvVariable";
|
||||||
|
import type { Env } from "~/types/env";
|
||||||
|
import {
|
||||||
|
AniListIdQuerySchema,
|
||||||
|
ErrorResponse,
|
||||||
|
ErrorResponseSchema,
|
||||||
|
SuccessResponseSchema,
|
||||||
|
} from "~/types/schema";
|
||||||
|
|
||||||
|
import { getEpisodesFromAnify } from "./getByAniListId/anify";
|
||||||
|
import { EpisodesResponse } from "./getByAniListId/episode";
|
||||||
|
|
||||||
|
const EpisodesResponseSchema = SuccessResponseSchema(EpisodesResponse);
|
||||||
|
|
||||||
|
const route = createRoute({
|
||||||
|
tags: ["aniplay", "episodes"],
|
||||||
|
summary: "Fetch episodes for a title",
|
||||||
|
operationId: "fetchEpisodes",
|
||||||
|
method: "get",
|
||||||
|
path: "/",
|
||||||
|
request: {
|
||||||
|
query: z.object({ aniListId: AniListIdQuerySchema }),
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: EpisodesResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: "Returns a list of episodes",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: ErrorResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: "Returns an empty list because episodes not found",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new OpenAPIHono<Env>();
|
||||||
|
|
||||||
|
app.openapi(route, async (c) => {
|
||||||
|
const aniListId = Number(c.req.query("aniListId"));
|
||||||
|
|
||||||
|
const episodes = await fetchFromMultipleSources([
|
||||||
|
() => {
|
||||||
|
const isAnifyEnabled = readEnvVariable<boolean>(c.env, "ENABLE_ANIFY");
|
||||||
|
return getEpisodesFromAnify(isAnifyEnabled, aniListId);
|
||||||
|
},
|
||||||
|
() =>
|
||||||
|
import("./getByAniListId/consumet").then(({ getEpisodesFromConsumet }) =>
|
||||||
|
getEpisodesFromConsumet(aniListId),
|
||||||
|
),
|
||||||
|
() =>
|
||||||
|
import("./getByAniListId/amvstrm").then(({ getEpisodesFromAmvstrm }) =>
|
||||||
|
getEpisodesFromAmvstrm(aniListId),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!episodes) {
|
||||||
|
return c.json(ErrorResponse, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.json({
|
||||||
|
success: true,
|
||||||
|
result: episodes,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app;
|
||||||
@@ -11,4 +11,10 @@ app.route(
|
|||||||
await import("./getEpisodeUrl").then((controller) => controller.default),
|
await import("./getEpisodeUrl").then((controller) => controller.default),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// TODO: Remove this route once v2 is ready
|
||||||
|
app.route(
|
||||||
|
"/",
|
||||||
|
await import("./getEpisodesLegacy").then((controller) => controller.default),
|
||||||
|
);
|
||||||
|
|
||||||
export default app;
|
export default app;
|
||||||
|
|||||||
@@ -23,6 +23,13 @@ app.route(
|
|||||||
"/search",
|
"/search",
|
||||||
await import("~/controllers/search").then((controller) => controller.default),
|
await import("~/controllers/search").then((controller) => controller.default),
|
||||||
);
|
);
|
||||||
|
// TODO: Remove this route once v2 is ready
|
||||||
|
app.route(
|
||||||
|
"/download",
|
||||||
|
await import("~/controllers/episodes/downloadLegacy").then(
|
||||||
|
(controller) => controller.default,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
// The OpenAPI documentation will be available at /doc
|
// The OpenAPI documentation will be available at /doc
|
||||||
app.doc("/openapi.json", {
|
app.doc("/openapi.json", {
|
||||||
|
|||||||
Reference in New Issue
Block a user