import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi"; import { env } from "hono/adapter"; import { updateWatchStatus } from "~/controllers/watch-status"; import type { Env } from "~/types/env"; import { AniListIdQuerySchema, EpisodeNumberSchema, ErrorResponse, ErrorResponseSchema, SuccessResponse, SuccessResponseSchema, } from "~/types/schema"; import { markEpisodeAsWatched } from "./anilist"; const MarkEpisodeAsWatchedRequest = z.object({ episodeNumber: EpisodeNumberSchema, isComplete: z.boolean(), }); const route = createRoute({ tags: ["aniplay", "episodes"], summary: "Mark episode as watched", operationId: "markEpisodeAsWatched", method: "post", path: "/{aniListId}/watched", request: { params: z.object({ aniListId: AniListIdQuerySchema }), body: { content: { "application/json": { schema: MarkEpisodeAsWatchedRequest, }, }, }, }, responses: { 200: { content: { "application/json": { schema: SuccessResponseSchema(), }, }, description: "Returns whether the episode was marked as watched", }, 401: { content: { "application/json": { schema: ErrorResponseSchema, }, }, description: "Unauthorized to mark the episode as watched", }, 500: { content: { "application/json": { schema: ErrorResponseSchema, }, }, description: "Error marking episode as watched", }, }, }); const app = new OpenAPIHono(); app.openapi(route, async (c) => { const aniListToken = c.req.header("X-AniList-Token"); if (!aniListToken) { return c.json(ErrorResponse, { status: 401 }); } const deviceId = c.req.header("X-Aniplay-DeviceId")!; const aniListId = Number(c.req.param("aniListId")); const { episodeNumber, isComplete } = await c.req.json(); try { await markEpisodeAsWatched( aniListToken, aniListId, episodeNumber, isComplete, ); if (isComplete) { await updateWatchStatus( env(c, "workerd"), c.req, deviceId, aniListId, "COMPLETED", ); } } catch (error) { console.error("Failed to mark episode as watched"); console.error(error); return c.json(ErrorResponse, { status: 500 }); } return c.json(SuccessResponse, 200); }); export default app;