From 92c1de3d1f224c641d9a03c3ed57b91a6eb5abd3 Mon Sep 17 00:00:00 2001 From: Pacharapol Withayasakpunt Date: Thu, 28 Apr 2022 12:59:34 +0700 Subject: [PATCH] add vocabulary to subjects --- scripts/get-kanji-level.ts | 6 +- src/wanikani.ts | 125 +++++++++++++++++++++++++------------ 2 files changed, 89 insertions(+), 42 deletions(-) diff --git a/scripts/get-kanji-level.ts b/scripts/get-kanji-level.ts index afd359a..904b4b3 100644 --- a/scripts/get-kanji-level.ts +++ b/scripts/get-kanji-level.ts @@ -9,14 +9,16 @@ interface ILevelMap { }; } -async function makeWaniKaniKanjiLevels(opts: { cache?: boolean } = {}) { +async function makeWaniKaniKanjiLevels( + opts: { cache?: boolean; force?: boolean } = {}, +) { const FILENAME = 'cache/wanikani-kanji.yaml'; if (opts.cache && existsSync(FILENAME)) { return yaml.load(readFileSync(FILENAME, 'utf-8')) as ILevelMap; } const wkKanji = await new WaniKani() - .subjects() + .subjects({ force: !!opts.force }) .then((vs) => vs.filter((v) => v.object === 'kanji') as IKanji[]); const levelMap: ILevelMap = {}; diff --git a/src/wanikani.ts b/src/wanikani.ts index c9fab42..2829b90 100644 --- a/src/wanikani.ts +++ b/src/wanikani.ts @@ -1,4 +1,4 @@ -import fs from 'fs'; +import { existsSync, readFileSync, writeFileSync } from 'fs'; import axios, { AxiosInstance } from 'axios'; @@ -20,18 +20,20 @@ export class WaniKani { force?: boolean; } = {}) { const subjects = { - data: [] as (IKanji | IRadical)[], + data: [] as ISubject[], filename: 'cache/wanikani.json', load() { - this.data = JSON.parse(fs.readFileSync(this.filename, 'utf-8')); + this.data = existsSync(this.filename) + ? JSON.parse(readFileSync(this.filename, 'utf-8')) + : []; return this.data; }, - dump(d: (IKanji | IRadical)[]) { + dump(d: ISubject[]) { this.data = d; this.finalize(); }, finalize() { - fs.writeFileSync(this.filename, JSON.stringify(this.data)); + writeFileSync(this.filename, JSON.stringify(this.data)); }, }; @@ -41,14 +43,14 @@ export class WaniKani { } data = []; - let nextURL = '/subjects?types=radical,kanji'; + let nextURL = '/subjects'; while (nextURL) { const r = await this.$axios .get<{ pages: { next_url?: string; }; - data: (IKanji | IRadical)[]; + data: ISubject[]; }>(nextURL) .then((r) => r.data); data.push(...r.data); @@ -61,52 +63,95 @@ export class WaniKani { } } -export interface IKanji { - id: number; - /** Actual type */ - object: 'kanji'; +type WaniKaniDate = string; +type Integer = number; +type Level = Integer; +type SubjectID = Integer; + +interface IMeaning { + meaning: string; + primary: boolean; +} + +interface ISubjectBase< + T extends 'radical' | 'kanji' | 'vocabulary', + Data extends {}, +> { + id: SubjectID; + object: T; url: string; - data_updated_at: string; + data_updated_at: WaniKaniDate; data: { - level: number; - document_url: string; + auxillary_meanings: IMeaning[]; characters: string; - meanings: { - meaning: string; - primary: boolean; + created_at: WaniKaniDate; + document_url: string; + hidden_at: WaniKaniDate | null; + lesson_position: Integer; + level: Level; + meaning_mnemonic: string; + meanings: IMeaning[]; + slug: string; + spaced_repetition_system_id: Integer; + } & Data; +} + +export type IRadical = ISubjectBase< + 'radical', + { + character_images: { + url: string; + metadata: { + inline_styles?: boolean; + dimensions?: string; + }; + content_type: string; }[]; + amalgamation_subject_ids: SubjectID[]; + } +>; + +export type IKanji = ISubjectBase< + 'kanji', + { readings: { reading: string; primary: boolean; type: 'kunyomi' | 'onyomi'; }[]; - component_subject_ids: number[]; - visually_similar_subject_ids: number[]; - }; -} + component_subject_ids: SubjectID[]; + visually_similar_subject_ids: SubjectID[]; + } +>; -export interface IRadical { - id: number; - /** Actual type */ - object: 'radical'; - url: string; - data_updated_at: string; - data: { - level: number; - document_url: string; - characters: string | null; - character_images: { +export type IVocabulary = ISubjectBase< + 'vocabulary', + { + component_subject_ids: SubjectID[]; + context_sentences: { + en: string; + ja: string; + }[]; + part_of_speech: string[]; + pronunciation_audios: { url: string; metadata: { - inline_styles?: boolean; - dimensions?: string; + gender: 'male' | 'female'; + source_id: Integer; + pronunciation: string; + voice_actor_id: Integer; + voice_actor_name: string; + voice_description: string; }; - content_type: string; + content_type: 'audio/mpeg' | 'audio/ogg'; }[]; - meanings: { - meaning: string; + readings: { + accepted_answer: boolean; primary: boolean; + reading: string; }[]; - amalgamation_subject_ids: number[]; - }; -} + reading_mnemonic: string; + } +>; + +export type ISubject = IRadical | IKanji | IVocabulary;