-
-
Save yunyu950908/59cfb290fdd00403b4a6305bf1a681bd to your computer and use it in GitHub Desktop.
将新标准日语初级和中级的音频和单词导入到 anki https://ankiweb.net/shared/info/1939635284
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const child_process = require('child_process'); | |
const path = require('path'); | |
const glob = require('glob'); | |
const crypto = require('crypto'); | |
const fs = require('fs-extra'); | |
const _ = require('lodash'); | |
const id3 = require('node-id3'); | |
const sort = require('alphanum-sort'); | |
const extractAudio = false; | |
function decryptDataV1(path, key) { | |
const encData = fs.readFileSync(path); | |
const iv = Buffer.alloc(128 / 8); | |
const decipher = crypto.createDecipheriv('aes-128-cbc', key, iv); | |
const data = []; | |
data.push(decipher.update(encData)); | |
data.push(decipher.final()); | |
return Buffer.concat(data).toString('utf8'); | |
} | |
function pad(str, n = 2) { | |
return _.padStart(str, n, '0'); | |
} | |
function processBook(name, cipherKey, bookId, basePath) { | |
let counter = 0; | |
const list = sort(glob.sync(path.join(basePath, '*/lesson*'))); | |
const outputDir = `./output/${name}/mp3`; | |
const notes = []; | |
fs.ensureDirSync(outputDir); | |
list.forEach(p => { | |
const m = p.match(/unit(\d+)\/lesson(\d+)/); | |
if (!m) { | |
throw new Error('Path not matching'); | |
} | |
const [ unit, lesson ] = m.slice(1); | |
const words = JSON.parse(decryptDataV1(path.join(p, 'words.dat'), cipherKey)) | |
.content | |
.filter(word => word.linetype === '1'); | |
words.forEach((word, idx) => { | |
const audioName = `book${pad(bookId)}-lesson${pad(lesson)}-${pad(idx)}.mp3`; | |
const audioPath = path.join(outputDir, audioName); | |
word.word = word.word.replace(/\<[^\>]+\>/g, ''); | |
word.word = word.word.replace(/(/g, '(').replace(/)/g, ')'); | |
console.log('unit %s, lesson %s, word %d / %d: %s', unit, lesson, idx, words.length, word.word); | |
const data = { | |
kana: word.word.trim().replace(/\([^\)]+\)/g, '').trim() || '/', | |
type: word.wordtype.replace(/[\[\]]/g, '').trim() || '/', | |
trans: word.trans.trim() || '/', | |
jp: word.word.trim() || '/', | |
lesson: pad(lesson), | |
id: pad(counter++, 4), | |
audio: audioName, | |
other: ' ', | |
}; | |
notes.push(data); | |
if (extractAudio) { | |
child_process.spawnSync('ffmpeg', [ | |
'-i', | |
path.join(p, 'lesson_words.pepm'), | |
'-codec:a', | |
'libmp3lame', | |
'-qscale:a', // reduce file size by setting a lower quality | |
'6', | |
'-ss', | |
word.starttime.trim(), | |
'-to', | |
word.endtime.trim(), | |
'-metadata', | |
`title="${word.word.trim()}"`, | |
'-id3v2_version', | |
'3', | |
'-write_id3v1', | |
'1', | |
'-y', | |
audioPath | |
], { shell: true }); | |
} | |
}); | |
}); | |
fs.writeFileSync(`./output/${name}/data.txt`, notes | |
.map(data => [data.id, data.kana, data.type, data.trans, data.jp, data.lesson, `[sound:${data.audio}]`].join('\t')) | |
.join('\n') | |
); | |
} | |
processBook('新标准日本语初级', '@@www.pep.com.cn', 1, './resources/book1'); | |
processBook('新标准日本语中级', '@@pepmres.com.cn', 2, './resources/book2'); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment