feat: Centralize Anilist GraphQL queries, generalize Durable Object for multiple operations with caching, and add new controllers for search, popular titles, user data, and episode tracking.
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import { graphql } from "gql.tada";
|
||||
import { GraphQLClient } from "graphql-request";
|
||||
import { env } from "cloudflare:workers";
|
||||
import type { HonoRequest } from "hono";
|
||||
import { DateTime } from "luxon";
|
||||
|
||||
@@ -7,38 +6,6 @@ import { maybeScheduleNextAiringEpisode } from "~/libs/maybeScheduleNextAiringEp
|
||||
import { getValue, setValue } from "~/models/kv";
|
||||
import { filterUnreleasedTitles } from "~/models/unreleasedTitles";
|
||||
import type { Title } from "~/types/title";
|
||||
import { MediaFragment } from "~/types/title/mediaFragment";
|
||||
|
||||
const GetUpcomingTitlesQuery = graphql(
|
||||
`
|
||||
query GetUpcomingTitles(
|
||||
$page: Int!
|
||||
$airingAtLowerBound: Int!
|
||||
$airingAtUpperBound: Int!
|
||||
) {
|
||||
Page(page: $page) {
|
||||
airingSchedules(
|
||||
notYetAired: true
|
||||
sort: TIME
|
||||
airingAt_lesser: $airingAtUpperBound
|
||||
airingAt_greater: $airingAtLowerBound
|
||||
) {
|
||||
id
|
||||
airingAt
|
||||
timeUntilAiring
|
||||
episode
|
||||
media {
|
||||
...Media
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
[MediaFragment],
|
||||
);
|
||||
|
||||
type AiringSchedule = {
|
||||
media: Title;
|
||||
@@ -49,7 +16,9 @@ type AiringSchedule = {
|
||||
};
|
||||
|
||||
export async function getUpcomingTitlesFromAnilist(req: HonoRequest) {
|
||||
const client = new GraphQLClient("https://graphql.anilist.co/");
|
||||
const durableObjectId = env.ANILIST_DO.idFromName("GLOBAL");
|
||||
const stub = env.ANILIST_DO.get(durableObjectId);
|
||||
|
||||
const lastCheckedScheduleAt = await getValue("schedule_last_checked_at").then(
|
||||
(value) => (value ? Number(value) : DateTime.now().toUnixInteger()),
|
||||
);
|
||||
@@ -61,21 +30,38 @@ export async function getUpcomingTitlesFromAnilist(req: HonoRequest) {
|
||||
let shouldContinue = true;
|
||||
|
||||
do {
|
||||
const { Page } = await client.request(GetUpcomingTitlesQuery, {
|
||||
page: currentPage++,
|
||||
airingAtLowerBound: lastCheckedScheduleAt,
|
||||
airingAtUpperBound: twoDaysFromNow,
|
||||
const response = await stub.fetch("http://anilist-do/graphql", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
operationName: "GetUpcomingTitles",
|
||||
variables: {
|
||||
page: currentPage++,
|
||||
airingAtLowerBound: lastCheckedScheduleAt,
|
||||
airingAtUpperBound: twoDaysFromNow,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const { airingSchedules, pageInfo } = Page!;
|
||||
if (!response.ok) {
|
||||
// If failed, break loop or handle error. For now, break.
|
||||
break;
|
||||
}
|
||||
|
||||
const Page = (await response.json()) as any;
|
||||
if (!Page) break;
|
||||
|
||||
const { airingSchedules, pageInfo } = Page;
|
||||
plannedToWatchTitles = plannedToWatchTitles.union(
|
||||
await filterUnreleasedTitles(
|
||||
airingSchedules!.map((schedule) => schedule!.media?.id!),
|
||||
airingSchedules!.map((schedule: any) => schedule!.media?.id!),
|
||||
),
|
||||
);
|
||||
scheduleList = scheduleList.concat(
|
||||
airingSchedules!.filter(
|
||||
(schedule): schedule is AiringSchedule =>
|
||||
(schedule: any): schedule is AiringSchedule =>
|
||||
!!schedule &&
|
||||
!plannedToWatchTitles.has(schedule.media?.id) &&
|
||||
schedule.media?.countryOfOrigin === "JP" &&
|
||||
|
||||
Reference in New Issue
Block a user