Browse Source

update sound populater

main
parent
commit
e486a76236
5 changed files with 127 additions and 96 deletions
  1. +6
    -86
      scripts/populate-from-wanikani.ts
  2. +1
    -1
      scripts/query-anki.ts
  3. +17
    -5
      src/anki.ts
  4. +1
    -1
      src/ankiconnect.ts
  5. +102
    -3
      src/wanikani.ts

+ 6
- 86
scripts/populate-from-wanikani.ts View File

@ -1,89 +1,9 @@
import { soundTag } from '@/anki';
import { AnkiConnect, IAnkiConnectActions } from '@/ankiconnect';
import { ISubject, IVocabulary, WaniKani } from '@/wanikani';
export async function populateSounds(
query: string,
jaField: string,
audioField: string,
subjects: ISubject[],
) {
const vocabularies = subjects.filter(
(s) => s.object === 'vocabulary',
) as IVocabulary[];
const audioMap = new Map<
string,
{
url: string;
filename: string;
}
>();
vocabularies.map((v) => {
if (audioMap.has(v.data.characters)) return;
const audio = v.data.pronunciation_audios[0];
if (!audio) return;
audioMap.set(v.data.characters, {
url: audio.url,
filename: `wanikani_${v.data.characters}_${audio.metadata.source_id}${
audio.content_type === 'audio/ogg' ? '.ogg' : '.mp3'
}`,
});
});
if (!audioMap.size) return;
query += ` -${jaField}: ${audioField}:`;
const anki = new AnkiConnect();
anki
.api('findNotes', {
query,
})
.then((notes) => anki.api('notesInfo', { notes }))
.then(async (notes) => {
const notesToUpdate: IAnkiConnectActions['updateNoteFields']['params']['note'][] =
[];
for (const n of notes) {
const { value: ja } = n.fields[jaField] || {};
if (ja) {
const audio = audioMap.get(
ja.replace(/\[.+?\]/g, '').replace(/ /g, ''),
);
if (audio) {
notesToUpdate.push({
id: n.noteId,
fields: {
[audioField]: soundTag.add(audio.filename),
},
audio: [
{
url: audio.url,
filename: audio.filename,
fields: [audioField],
},
],
});
}
}
}
if (!notesToUpdate.length) return;
while (notesToUpdate.length) {
await anki.multi<'updateNoteFields'[]>({
actions: notesToUpdate.splice(0, 100).map((note) => ({
action: 'updateNoteFields',
params: {
note,
},
})),
});
}
});
}
import { WaniKani } from '@/wanikani';
if (require.main === module) {
new WaniKani().subjects().then((subjects) => {
populateSounds('note:jp.takoboto', 'Japanese', 'JapaneseAudio', subjects);
});
new WaniKani().populateSound(
'note:jp.takoboto',
{ ja: 'Japanese', audio: 'JapaneseAudio' },
{ mode: { online: true } },
);
}

+ 1
- 1
scripts/query-anki.ts View File

@ -40,7 +40,7 @@ export async function addSoundTag(query: string, fieldNames: string[]) {
const { value } = field;
if (!soundTag.is(value)) {
toUpdate = toUpdate || { id: n.noteId, fields: {} };
toUpdate.fields[fieldName] = soundTag.add(value);
toUpdate.fields[fieldName] = soundTag.make(value);
}
}
});

+ 17
- 5
src/anki.ts View File

@ -53,16 +53,28 @@ export const soundTag = {
is(src: string) {
return src.startsWith(this.pre) && src.endsWith(this.post);
},
add(src: string) {
if (soundTag.is(src)) return src;
return this.pre + src + this.post;
make(src: string) {
if (this.is(src)) return src;
const out = this.pre + src + this.post;
if (this.isBroken(out)) return src;
return out;
},
remove(src: string) {
if (!soundTag.is(src)) return src;
clean(src: string) {
if (this.isBroken(src)) return src;
if (this.isEmpty(src)) return src;
return src.substring(this.pre.length, src.length - this.post.length);
},
isEmpty(src: string) {
return src === this.pre + this.post;
},
isBroken(src: string) {
if (this.is(src)) {
return (
src.indexOf(this.pre) > 0 ||
src.indexOf(this.post) < src.length - this.post.length
);
}
return false;
},
};

+ 1
- 1
src/ankiconnect.ts View File

@ -266,7 +266,7 @@ export interface IAnkiConnectActions
params: {
note: {
id: number;
fields: {
fields?: {
[fieldName: string]: string;
};
audio?: IAnkiConnectMediaInNote[];

+ 102
- 3
src/wanikani.ts View File

@ -2,14 +2,18 @@ import { existsSync, readFileSync, writeFileSync } from 'fs';
import axios, { AxiosInstance } from 'axios';
import { soundTag } from './anki';
import { AnkiConnect, IAnkiConnectActions } from './ankiconnect';
import { logger } from './logger';
export class WaniKani {
$axios: AxiosInstance;
constructor(public apiKey = process.env['WANIKANI_API_KEY']!) {
constructor(public apiKey = process.env['WANIKANI_API_KEY']) {
this.$axios = axios.create({
baseURL: 'https://api.wanikani.com/v2/',
headers: {
Authorization: `Bearer ${process.env['WANIKANI_API_KEY']}`,
Authorization: apiKey ? `Bearer ${apiKey}` : '',
},
});
}
@ -54,13 +58,108 @@ export class WaniKani {
}>(nextURL)
.then((r) => r.data);
data.push(...r.data);
console.info(r.pages.next_url);
logger('WaniKani API:', r.pages.next_url);
nextURL = r.pages.next_url || '';
}
subjects.dump(data);
return data;
}
async populateSound(
query: string,
fields: {
ja: string;
audio: string;
},
opts: {
mode?: {
online?: boolean;
};
} = {},
) {
const subjects = await this.subjects();
const vocabularies = subjects.filter(
(s) => s.object === 'vocabulary',
) as IVocabulary[];
const audioMap = new Map<
string,
{
url: string;
filename: string;
}
>();
vocabularies.map((v) => {
if (audioMap.has(v.data.characters)) return;
const audio = v.data.pronunciation_audios[0];
if (!audio) return;
audioMap.set(v.data.characters, {
url: audio.url,
filename: `wanikani_${v.data.characters}_${audio.metadata.source_id}${
audio.content_type === 'audio/ogg' ? '.ogg' : '.mp3'
}`,
});
});
if (!audioMap.size) return;
query += ` -${fields.ja}: ${fields.audio}:`;
const anki = new AnkiConnect();
anki
.api('findNotes', {
query,
})
.then((notes) => anki.api('notesInfo', { notes }))
.then(async (notes) => {
const notesToUpdate: IAnkiConnectActions['updateNoteFields']['params']['note'][] =
[];
for (const n of notes) {
const { value: ja } = n.fields[fields.ja] || {};
if (ja) {
const audio = audioMap.get(
ja.replace(/\[.+?\]/g, '').replace(/ /g, ''),
);
if (audio) {
notesToUpdate.push(
opts.mode?.online
? {
id: n.noteId,
fields: {
[fields.audio]: soundTag.make(audio.url),
},
}
: {
id: n.noteId,
fields: {},
audio: [
{
url: audio.url,
filename: audio.filename,
fields: [fields.audio],
},
],
},
);
}
}
}
if (!notesToUpdate.length) return;
while (notesToUpdate.length) {
await anki.multi<'updateNoteFields'[]>({
actions: notesToUpdate.splice(0, 100).map((note) => ({
action: 'updateNoteFields',
params: {
note,
},
})),
});
}
});
}
}
type WaniKaniDate = string;

Loading…
Cancel
Save