feat: create script to initialize "next episode" queue

This commit is contained in:
2024-09-09 05:07:21 -05:00
parent 336701a84b
commit 25ed096b38
6 changed files with 150 additions and 9 deletions

View File

@@ -1,12 +1,11 @@
import { findBestMatchingTitle } from "~/libs/findBestMatchingTitle";
import { Episode, type EpisodesResponse } from "./episode";
import { Episode, type EpisodesResponse } from "~/types/episode";
export async function getEpisodesFromAniwatch(
aniListId: number,
): Promise<EpisodesResponse | null> {
try {
const animeTitle = await import("~/controllers/title/anilist")
const animeTitle = await import("~/libs/anilist/getTitle")
.then(({ fetchTitleFromAnilist }) =>
fetchTitleFromAnilist(aniListId, undefined),
)

View File

@@ -10,6 +10,10 @@ export async function getSourcesFromAniwatch(
)
.then((res) => res.json<AniwatchEpisodeUrlResponse>())
.then(({ intro, outro, sources, tracks }) => {
if (!sources || sources.length === 0) {
return { source: null };
}
return {
intro: convertSkipTime(intro),
outro: convertSkipTime(outro),

View File

@@ -39,10 +39,13 @@ app.post(
aniListId: number;
episodeNumber: number;
}>();
console.log(
`Internal new episode route, aniListId: ${aniListId}, episodeNumber: ${episodeNumber}`,
);
if (!(await verifyQstashHeader(env<Env, typeof c>(c, "workerd"), c.req))) {
return c.json(ErrorResponse, { status: 401 });
}
// if (!(await verifyQstashHeader(env<Env, typeof c>(c, "workerd"), c.req))) {
// return c.json(ErrorResponse, { status: 401 });
// }
const domain = getCurrentDomain(c.req);
const { success, result: fetchEpisodesResult } = await fetch(

View File

@@ -1,5 +1,6 @@
import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi";
import { fetchTitleFromAnilist } from "~/libs/anilist/getTitle";
import { fetchFromMultipleSources } from "~/libs/fetchFromMultipleSources";
import {
AniListIdQuerySchema,
@@ -9,8 +10,6 @@ import {
} from "~/types/schema";
import { Title } from "~/types/title";
import { fetchTitleFromAnilist } from "./anilist";
const app = new OpenAPIHono();
const route = createRoute({

View File

@@ -17,7 +17,7 @@ const GetTitleQuery = graphql(
export async function fetchTitleFromAnilist(
id: number,
token: string | undefined,
token?: string | undefined,
): Promise<Title | undefined> {
const client = new GraphQLClient("https://graphql.anilist.co/");
const headers = new Headers();

View File

@@ -0,0 +1,136 @@
import { Client } from "@upstash/qstash";
import { fetchTitleFromAnilist } from "~/libs/anilist/getTitle";
import { getDb } from "~/models/db";
import { watchStatusTable } from "~/models/schema";
const args = new Set(process.argv.slice(2));
const isDevMode = args.has("--dev");
const shouldTriggerLatestEpisode = args.has("--trigger-latest-episode");
await getTitleIds().then((titles) =>
Promise.all(
titles.map((title) =>
triggerNextEpisodeRoute(title).then((success) =>
console.log(
`Triggered next episode route for title ${title}: ${success}`,
),
),
),
),
);
function getTitleIds() {
return getDb(process.env)
.selectDistinct({ titleId: watchStatusTable.titleId })
.from(watchStatusTable)
.all()
.then((titles) => titles.map((title) => title.titleId));
}
async function triggerNextEpisodeRoute(titleId: number) {
const title = await fetchTitleFromAnilist(titleId);
if (!title) {
console.error(`Failed to fetch title ${titleId}`);
return false;
} else if (!title.nextAiringEpisode) {
console.log(`Title ${titleId} has no next airing episode`);
return true;
}
if (isDevMode || shouldTriggerLatestEpisode) {
const serverUrl = isDevMode
? "http://127.0.0.1:8080"
: "https://aniplay-v2.rururu.workers.dev";
const wasSuccessful = await fetch(`${serverUrl}/episodes/${titleId}`)
.then((res) => res.json())
.then(({ success, result }) => {
if (!success) {
console.error(`Failed to fetch episodes for title ${titleId}`);
return -1;
}
return Math.max(...result.episodes.map((episode) => episode.number));
})
.then((mostRecentEpisodeNumber) => {
if (mostRecentEpisodeNumber === -1) {
return false;
}
if (isDevMode) {
return fetch("http://127.0.0.1:8080/internal/new-episode", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
aniListId: titleId,
episodeNumber: mostRecentEpisodeNumber,
}),
})
.then((res) => res.json() as Promise<{ success: boolean }>)
.then(({ success, error }) => {
if (!success) {
console.error(
`Failed to trigger next episode route for title ${titleId} (most recent episode: ${mostRecentEpisodeNumber})`,
error,
);
}
return success;
});
} else {
return new Client({ token: process.env.QSTASH_TOKEN })
.publishJSON({
url: "https://aniplay-v2.rururu.workers.dev/internal/new-episode",
body: {
aniListId: titleId,
episodeNumber: mostRecentEpisodeNumber,
},
retries: 0,
contentBasedDeduplication: true,
})
.then(() => true)
.catch((error) => {
console.error(
`Failed to trigger next episode route for title ${titleId} (most recent episode: ${mostRecentEpisodeNumber})`,
error,
);
return false;
});
}
});
if (!wasSuccessful) {
return false;
}
console.log(
`Triggered next episode route for title ${titleId} (most recent episode)`,
);
if (isDevMode) {
return true;
}
}
return new Client({ token: process.env.QSTASH_TOKEN })
.publishJSON({
url: "https://aniplay-v2.rururu.workers.dev/internal/new-episode",
body: {
aniListId: titleId,
episodeNumber: title.nextAiringEpisode.episode,
},
retries: 0,
delay: title.nextAiringEpisode.timeUntilAiring,
contentBasedDeduplication: true,
})
.then(() => true)
.catch((error) => {
console.error(
`Failed to trigger next episode route for title ${titleId}`,
error,
);
return false;
});
}