feat: create script to initialize "next episode" queue
This commit is contained in:
@@ -1,12 +1,11 @@
|
|||||||
import { findBestMatchingTitle } from "~/libs/findBestMatchingTitle";
|
import { findBestMatchingTitle } from "~/libs/findBestMatchingTitle";
|
||||||
|
import { Episode, type EpisodesResponse } from "~/types/episode";
|
||||||
import { Episode, type EpisodesResponse } from "./episode";
|
|
||||||
|
|
||||||
export async function getEpisodesFromAniwatch(
|
export async function getEpisodesFromAniwatch(
|
||||||
aniListId: number,
|
aniListId: number,
|
||||||
): Promise<EpisodesResponse | null> {
|
): Promise<EpisodesResponse | null> {
|
||||||
try {
|
try {
|
||||||
const animeTitle = await import("~/controllers/title/anilist")
|
const animeTitle = await import("~/libs/anilist/getTitle")
|
||||||
.then(({ fetchTitleFromAnilist }) =>
|
.then(({ fetchTitleFromAnilist }) =>
|
||||||
fetchTitleFromAnilist(aniListId, undefined),
|
fetchTitleFromAnilist(aniListId, undefined),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ export async function getSourcesFromAniwatch(
|
|||||||
)
|
)
|
||||||
.then((res) => res.json<AniwatchEpisodeUrlResponse>())
|
.then((res) => res.json<AniwatchEpisodeUrlResponse>())
|
||||||
.then(({ intro, outro, sources, tracks }) => {
|
.then(({ intro, outro, sources, tracks }) => {
|
||||||
|
if (!sources || sources.length === 0) {
|
||||||
|
return { source: null };
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
intro: convertSkipTime(intro),
|
intro: convertSkipTime(intro),
|
||||||
outro: convertSkipTime(outro),
|
outro: convertSkipTime(outro),
|
||||||
|
|||||||
@@ -39,10 +39,13 @@ app.post(
|
|||||||
aniListId: number;
|
aniListId: number;
|
||||||
episodeNumber: number;
|
episodeNumber: number;
|
||||||
}>();
|
}>();
|
||||||
|
console.log(
|
||||||
|
`Internal new episode route, aniListId: ${aniListId}, episodeNumber: ${episodeNumber}`,
|
||||||
|
);
|
||||||
|
|
||||||
if (!(await verifyQstashHeader(env<Env, typeof c>(c, "workerd"), c.req))) {
|
// if (!(await verifyQstashHeader(env<Env, typeof c>(c, "workerd"), c.req))) {
|
||||||
return c.json(ErrorResponse, { status: 401 });
|
// return c.json(ErrorResponse, { status: 401 });
|
||||||
}
|
// }
|
||||||
|
|
||||||
const domain = getCurrentDomain(c.req);
|
const domain = getCurrentDomain(c.req);
|
||||||
const { success, result: fetchEpisodesResult } = await fetch(
|
const { success, result: fetchEpisodesResult } = await fetch(
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi";
|
import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi";
|
||||||
|
|
||||||
|
import { fetchTitleFromAnilist } from "~/libs/anilist/getTitle";
|
||||||
import { fetchFromMultipleSources } from "~/libs/fetchFromMultipleSources";
|
import { fetchFromMultipleSources } from "~/libs/fetchFromMultipleSources";
|
||||||
import {
|
import {
|
||||||
AniListIdQuerySchema,
|
AniListIdQuerySchema,
|
||||||
@@ -9,8 +10,6 @@ import {
|
|||||||
} from "~/types/schema";
|
} from "~/types/schema";
|
||||||
import { Title } from "~/types/title";
|
import { Title } from "~/types/title";
|
||||||
|
|
||||||
import { fetchTitleFromAnilist } from "./anilist";
|
|
||||||
|
|
||||||
const app = new OpenAPIHono();
|
const app = new OpenAPIHono();
|
||||||
|
|
||||||
const route = createRoute({
|
const route = createRoute({
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const GetTitleQuery = graphql(
|
|||||||
|
|
||||||
export async function fetchTitleFromAnilist(
|
export async function fetchTitleFromAnilist(
|
||||||
id: number,
|
id: number,
|
||||||
token: string | undefined,
|
token?: string | undefined,
|
||||||
): Promise<Title | undefined> {
|
): Promise<Title | undefined> {
|
||||||
const client = new GraphQLClient("https://graphql.anilist.co/");
|
const client = new GraphQLClient("https://graphql.anilist.co/");
|
||||||
const headers = new Headers();
|
const headers = new Headers();
|
||||||
136
src/scripts/initializeNextEpisodeQueue.ts
Normal file
136
src/scripts/initializeNextEpisodeQueue.ts
Normal 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;
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user