feat: migrate to cloudflare d1 and queues

This commit is contained in:
2025-11-28 16:32:35 +08:00
parent 00e1f82d85
commit bd958fb1ab
19 changed files with 294 additions and 276 deletions

View File

@@ -159,7 +159,7 @@ app.openapi(route, async (c) => {
mediaListEntry.status,
);
if (wasAdded) {
await maybeScheduleNextAiringEpisode(c.req, media.id);
await maybeScheduleNextAiringEpisode(media.id);
}
}

View File

@@ -2,6 +2,7 @@ import { zValidator } from "@hono/zod-validator";
import { Hono } from "hono";
import { z } from "zod";
import { getEpisodesFromAniwatch } from "~/controllers/episodes/getByAniListId/aniwatch";
import { fetchEpisodeUrl } from "~/controllers/episodes/getEpisodeUrl";
import { getAdminSdkCredentials } from "~/libs/gcloud/getAdminSdkCredentials";
import { sendFcmMessage } from "~/libs/gcloud/sendFcmMessage";
@@ -16,6 +17,46 @@ import {
const app = new Hono();
export async function onNewEpisode(aniListId: number, episodeNumber: number) {
console.log(
`On new episode, aniListId: ${aniListId}, episodeNumber: ${episodeNumber}`,
);
if (!(await isWatchingTitle(aniListId))) {
console.log(`Title ${aniListId} is no longer being watched`);
return { success: true, result: { isNoLongerWatching: true } };
}
const episodes = await getEpisodesFromAniwatch(aniListId);
const fetchUrlResult = await fetchEpisodeUrl({ aniListId, episodeNumber });
if (!fetchUrlResult) {
console.error(`Failed to fetch episode URL for episode`);
return { success: false, message: "Failed to fetch episode URL" };
}
const tokens = await getTokensSubscribedToTitle(aniListId);
await Promise.allSettled(
tokens.map(async (token) => {
return sendFcmMessage(getAdminSdkCredentials(), {
token,
data: {
type: "new_episode",
episodes: JSON.stringify(episodes),
episodeStreamInfo: JSON.stringify(fetchUrlResult),
aniListId: aniListId.toString(),
episodeNumber: episodeNumber.toString(),
},
android: { priority: "high" },
});
}),
);
await maybeScheduleNextAiringEpisode(aniListId);
return SuccessResponse;
}
app.post(
"/",
zValidator(
@@ -30,48 +71,13 @@ app.post(
aniListId: number;
episodeNumber: number;
}>();
console.log(
`Internal new episode route, aniListId: ${aniListId}, episodeNumber: ${episodeNumber}`,
);
if (!(await isWatchingTitle(aniListId))) {
console.log(`Title ${aniListId} is no longer being watched`);
return c.json(
{ success: true, result: { isNoLongerWatching: true } },
200,
);
const result = await onNewEpisode(aniListId, episodeNumber, c.req);
if (result.success) {
return c.json(result, 200);
} else {
return c.json(result, 500);
}
const fetchUrlResult = await fetchEpisodeUrl({ aniListId, episodeNumber });
if (!fetchUrlResult) {
console.error(`Failed to fetch episode URL for episode`);
return c.json(
{ success: false, message: "Failed to fetch episode URL" },
500,
);
}
const tokens = await getTokensSubscribedToTitle(aniListId);
await Promise.allSettled(
tokens.map(async (token) => {
return sendFcmMessage(getAdminSdkCredentials(), {
token,
data: {
type: "new_episode",
episodes: JSON.stringify(episodes),
episodeStreamInfo: JSON.stringify(fetchUrlResult),
aniListId: aniListId.toString(),
episodeNumber: episodeNumber.toString(),
},
android: { priority: "high" },
});
}),
);
await maybeScheduleNextAiringEpisode(c.req, aniListId);
return c.json(SuccessResponse, 200);
},
);

View File

@@ -87,7 +87,7 @@ export async function getUpcomingTitlesFromAnilist(req: HonoRequest) {
await Promise.all(
Array.from(plannedToWatchTitles).map((titleId) =>
maybeScheduleNextAiringEpisode(req, titleId),
maybeScheduleNextAiringEpisode(titleId),
),
);

View File

@@ -8,6 +8,6 @@ export const maybeUpdateLastConnectedAt = createMiddleware(async (c, next) => {
return next();
}
await updateDeviceLastConnectedAt(c.env, deviceId);
await updateDeviceLastConnectedAt(deviceId);
return next();
});

View File

@@ -1,6 +1,7 @@
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";
@@ -76,9 +77,9 @@ export async function updateWatchStatus(
watchStatus,
);
if (wasAdded) {
await maybeScheduleNextAiringEpisode(req, titleId);
await maybeScheduleNextAiringEpisode(titleId);
} else if (wasDeleted) {
await removeTask("new-episode", buildNewEpisodeTaskId(titleId));
await removeTask("NEW_EPISODE", buildNewEpisodeTaskId(titleId));
}
}
@@ -115,13 +116,12 @@ app.openapi(route, async (c) => {
}
await queueTask(
"anilist-updates",
"ANILIST_UPDATES",
{
deviceId,
watchStatus,
titleId,
isRetrying: true,
nameSuffix: "watch-status",
updateType: AnilistUpdateType.UpdateWatchStatus,
},
{ req: c.req, scheduleConfig: { delay: { minute: 1 } } },
);