import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi"; import type { HonoRequest } from "hono"; import { AnilistUpdateType } from "~/libs/anilist/updateType.ts"; import { maybeScheduleNextAiringEpisode } from "~/libs/maybeScheduleNextAiringEpisode"; import { buildNewEpisodeTaskId } from "~/libs/tasks/id"; import { queueTask } from "~/libs/tasks/queueTask"; import { removeTask } from "~/libs/tasks/removeTask"; import { setWatchStatus } from "~/models/watchStatus"; import { AniListIdSchema, ErrorResponse, ErrorResponseSchema, SuccessResponse, SuccessResponseSchema, } from "~/types/schema"; import { WatchStatus } from "~/types/title/watchStatus"; const app = new OpenAPIHono(); const UpdateWatchStatusRequest = z.object({ deviceId: z.string(), watchStatus: WatchStatus.nullable(), titleId: AniListIdSchema, isRetrying: z.boolean().optional().default(false), }); const route = createRoute({ tags: ["aniplay", "title"], operationId: "updateWatchStatus", summary: "Update watch status for a title", description: "Updates the watch status for a title. If the user sets the watch status to 'watching', they'll start getting notified about new episodes.", method: "post", path: "/", request: { body: { content: { "application/json": { schema: UpdateWatchStatusRequest, }, }, }, headers: z.object({ "x-anilist-token": z.string().nullish() }), }, responses: { 200: { content: { "application/json": { schema: SuccessResponseSchema(), }, }, description: "Watch status was successfully updated", }, 500: { content: { "application/json": { schema: ErrorResponseSchema, }, }, description: "Failed to update watch status", }, }, }); export async function updateWatchStatus( deviceId: string, titleId: number, watchStatus: WatchStatus | null, ) { const { wasAdded, wasDeleted } = await setWatchStatus( deviceId, Number(titleId), watchStatus, ); if (wasAdded) { await maybeScheduleNextAiringEpisode(titleId); } else if (wasDeleted) { await removeTask("NEW_EPISODE", buildNewEpisodeTaskId(titleId)); } } app.openapi(route, async (c) => { const { deviceId, watchStatus, titleId, isRetrying = false, } = await c.req.json(); const aniListToken = c.req.header("X-AniList-Token"); // Check if we should use mock data const { useMockData } = await import("~/libs/useMockData"); if (useMockData()) { // Return success immediately without side effects return c.json(SuccessResponse, { status: 200 }); } if (!isRetrying) { try { await updateWatchStatus(deviceId, titleId, watchStatus); } catch (error) { console.error("Error setting watch status"); console.error(error); return c.json(ErrorResponse, { status: 500 }); } } await queueTask( "ANILIST_UPDATES", { deviceId, watchStatus, titleId, updateType: AnilistUpdateType.UpdateWatchStatus, }, { req: c.req, scheduleConfig: { delay: { minute: 1 } } }, ); return c.json(SuccessResponse, { status: 200 }); }); export default app;