Files
aniplay-api/src/controllers/watch-status/index.ts
Rushil Perera 8175d73df1 refactor: ♻️emoves Env parameter
Removes the `Env` parameter from several functions to simplify their signatures and rely on the global `env` for configuration.

This change reduces the number of arguments passed around, making the code cleaner and easier to maintain.
2025-08-10 19:22:14 -04:00

134 lines
3.3 KiB
TypeScript

import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi";
import type { HonoRequest } from "hono";
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";
import { maybeUpdateWatchStatusOnAnilist } from "./anilist";
const app = new OpenAPIHono<Cloudflare.Env>();
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(
req: HonoRequest,
deviceId: string,
titleId: number,
watchStatus: WatchStatus | null,
) {
const { wasAdded, wasDeleted } = await setWatchStatus(
deviceId,
Number(titleId),
watchStatus,
);
if (wasAdded) {
await maybeScheduleNextAiringEpisode(req, 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<typeof UpdateWatchStatusRequest._type>();
const aniListToken = c.req.header("X-AniList-Token");
if (!isRetrying) {
try {
await updateWatchStatus(c.req, deviceId, titleId, watchStatus);
} catch (error) {
console.error("Error setting watch status");
console.error(error);
return c.json(ErrorResponse, { status: 500 });
}
}
try {
await maybeUpdateWatchStatusOnAnilist(
Number(titleId),
watchStatus,
aniListToken,
);
} catch (error) {
console.error("Failed to update watch status on Anilist");
console.error(error);
if (isRetrying) {
return c.json(ErrorResponse, { status: 500 });
}
await queueTask(
"anilist-updates",
{
deviceId,
watchStatus,
titleId,
isRetrying: true,
nameSuffix: "watch-status",
},
{ req: c.req, scheduleConfig: { delay: { minute: 1 } } },
);
}
return c.json(SuccessResponse, { status: 200 });
});
export default app;