diff options
Diffstat (limited to 'src/util/animeLoader.ts')
-rw-r--r-- | src/util/animeLoader.ts | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/src/util/animeLoader.ts b/src/util/animeLoader.ts new file mode 100644 index 0000000..120123b --- /dev/null +++ b/src/util/animeLoader.ts @@ -0,0 +1,93 @@ +import { readFile } from "fs/promises"; +import { z } from "zod"; +const zodType = z.array( + z.strictObject({ + animeId: z.number().int().min(0), + title: z.string(), + primaryCategory: z.string(), + startSeason: z.string(), + titleReviewScore: z.object({ + story: z.number().min(0).nullable(), + sakuga: z.number().min(0).nullable(), + character: z.number().min(0).nullable(), + music: z.number().min(0).nullable(), + originality: z.number().min(0).nullable(), + storyboard: z.number().min(0).nullable(), + voice: z.number().min(0).nullable(), + song: z.number().min(0).nullable(), + manzokudo: z.number().min(0).nullable(), + }), + titleReviewList: z.array( + z.object({ + reviewId: z.number().int(), + userIconUrl: z.string().url(), + userName: z.string(), + score: z.number(), + isSpoiler: z.boolean(), + timestampSec: z.number().int().min(0), + commentCount: z.number().int().min(0), + iineCount: z.number().int().min(0) + }), + ), + titleHitokotoList: z.array( + z.object({ + hitokotoId: z.number().int(), + userIcon: z.string().url(), + userName: z.string(), + reviewHtml: z.string(), + timestampSec: z.number().int(), + }) + ), + episodeList: z.array(z.object({ + episodeId: z.number().int().min(0), + subTitle: z.string(), + reviewCount: z.number().int().min(0), + score: z.number().nullable(), + })), + episodeHitokotoList: z.array(z.object({ + episodeId: z.number().int().min(0), + hitokotoList: z.array(z.object({ + hitokotoId: z.number().int().min(0), + userIcon: z.string(), + userName: z.string().min(1), + reviewHtml: z.string().min(1), + timestampSec: z.number().int().min(0), + })) + })), + }) +); +const MAX_ITEM_LIMIT = process.env["AKIBA_SOUKEN_MAX_ITEM_LIMIT"]; +export type AnimeLoaderData = z.infer<typeof zodType>[number] +class AnimeLoader { + constructor() { } + private dataCache: Awaited<ReturnType<AnimeLoader["loadData_"]>> | null = null; + async loadData() { + if (this.dataCache != null) { + return this.dataCache; + } + const loadedData = await this.loadData_(); + this.dataCache = loadedData; + return loadedData; + } + private async loadData_() { + const articleJsonPath = process.env["AKIBA_SOUKEN_ANIME_JSON"]!; + //console.log(`[${articleJsonPath}]`); + const jsonStr = await readFile(articleJsonPath, { encoding: "utf-8" }).then(text => { + const jsonObj = JSON.parse(text); + return jsonObj; + }); + const parsedObj = zodType.parse(jsonStr).filter(a => { + if (a.titleHitokotoList.length != 0) { return true; } + if (a.titleReviewList.length != 0) { return true; } + if (a.titleReviewScore.manzokudo != null) { return true; } + if (a.episodeList.find(e => e.score != null || e.reviewCount != 0)) { return true; } + if (a.episodeHitokotoList.length != 0) { return true; } + return false; + }); + if (MAX_ITEM_LIMIT != null) { + parsedObj.length = Math.min(Number(MAX_ITEM_LIMIT), parsedObj.length); + } + return parsedObj; + } +} +export const animeLoader = new AnimeLoader();
\ No newline at end of file |