lingva-tts/index.mjs

85 lines
2.2 KiB
JavaScript
Raw Normal View History

2023-02-11 23:08:50 +08:00
#!/usr/bin/env node
import { AudioContext } from 'web-audio-api'
import commandLineArgs from 'command-line-args'
import fetch from 'node-fetch'
const optionDefinitions = [
{ name: 'lang', type: String },
{ name: 'query', type: String },
{ name: 'stdin', type: Boolean }
]
const options = commandLineArgs(optionDefinitions)
if (options.stdin) {
handlePipedContent(data => {
options.query = data
const queries = splitString(options.query)
getAudio(options.lang, queries).then(audio => {
play(audio)
})
})
} else if (typeof options.query !== 'undefined') {
const queries = splitString(options.query)
getAudio(options.lang, queries).then(audio => {
play(audio)
})
} else {
console.log(
'At least one of the options --query or --stdin must be specified.'
)
process.exit(1)
}
function handlePipedContent (cb) {
let data = ''
process.stdin.on('readable', () => {
const chuck = process.stdin.read()
if (chuck !== null) {
data += chuck
}
})
process.stdin.on('end', () => cb(data))
}
function getAudio (lang, queries) {
const fetches = queries.map(query => {
return fetch(
'https://translate.yanknil.com/api/v1/audio/' +
lang +
'/' +
encodeURIComponent(query)
).then(res => res.json())
})
return Promise.all(fetches).then(results => {
return results.flatMap(result => result.audio)
})
}
function play (audio) {
const context = new AudioContext()
context.sampleRate = 24000
context.outStream = process.stdout
context.decodeAudioData(new Uint8Array(audio).buffer, audioBuffer => {
const source = context.createBufferSource()
source.connect(context.destination)
source.buffer = audioBuffer
source.start(0)
source.onended = () => {
process.exit()
}
})
}
function splitString (str = '') {
str = str.replace(/[\r\n]/g, '')
const regex = /([\u3000-\u303f\u3040-\u309f\u30a0-\u30ff\uff00-\uff9f\u4e00-\u9faf\u3400-\u4dbf]{1,199}|\S[^\u3000-\u303f\u3040-\u309f\u30a0-\u30ff\uff00-\uff9f\u4e00-\u9faf\u3400-\u4dbf]{1,197}\S(?= |$)|.{1,198}[\u3000-\u303f\u3040-\u309f\u30a0-\u30ff\uff00-\uff9f\u4e00-\u9faf\u3400-\u4dbf])/gm
const chunks = str.match(regex)
return chunks
}