138 lines
3.0 KiB
JavaScript
138 lines
3.0 KiB
JavaScript
require("dotenv").config()
|
|
|
|
const path = require("node:path")
|
|
const fs = require("node:fs")
|
|
const axios = require("axios")
|
|
const { aws4Interceptor } = require("aws4-axios")
|
|
|
|
const express = require("express")
|
|
const cors = require("cors")
|
|
|
|
const getPhrases = require("../../node_lib")
|
|
|
|
let app = null
|
|
|
|
const { LISTENING_PORT } = process.env
|
|
const PORT = LISTENING_PORT || 3000
|
|
|
|
const cachePath = path.join(process.cwd(), ".cache")
|
|
const audiosPath = path.join(cachePath, "audio")
|
|
|
|
const PollyBaseURL = "https://polly.us-east-1.amazonaws.com"
|
|
|
|
const PollyDefaultConfig = {
|
|
Engine: "generative",
|
|
VoiceId: "Lucia",
|
|
OutputFormat: "mp3",
|
|
LanguageCode: "es-ES",
|
|
}
|
|
|
|
const interceptor = aws4Interceptor({
|
|
options: {
|
|
region: "us-east-1",
|
|
service: "polly",
|
|
},
|
|
credentials: {
|
|
accessKeyId: process.env.AWS_API_KEY,
|
|
secretAccessKey: process.env.AWS_API_SECRET,
|
|
},
|
|
})
|
|
|
|
axios.interceptors.request.use(interceptor)
|
|
|
|
async function synthesizePollyVoice(phrase, phraseId) {
|
|
if (!fs.existsSync(audiosPath)) {
|
|
fs.mkdirSync(audiosPath, { recursive: true })
|
|
}
|
|
|
|
// call to api and stream result to a file
|
|
const voiceResultPath = path.resolve(audiosPath, phraseId + ".mp3")
|
|
|
|
if (fs.existsSync(voiceResultPath)) {
|
|
fs.unlinkSync(voiceResultPath)
|
|
}
|
|
|
|
const voiceResultFile = fs.createWriteStream(voiceResultPath)
|
|
|
|
console.log(`Catching TTS file for id [${phraseId}]`)
|
|
|
|
const { data: stream } = await axios({
|
|
url: `${PollyBaseURL}/v1/speech`,
|
|
method: "POST",
|
|
data: {
|
|
...PollyDefaultConfig,
|
|
Text: phrase,
|
|
},
|
|
responseType: "stream",
|
|
})
|
|
|
|
stream.pipe(voiceResultFile)
|
|
|
|
return new Promise((resolve, reject) => {
|
|
stream.on("end", () => resolve(voiceResultPath))
|
|
stream.on("error", (error) => reject(error))
|
|
})
|
|
}
|
|
|
|
async function fetchTTSAudioURL(req, phrase, phraseId) {
|
|
const filePath = path.join(audiosPath, `${phraseId}.mp3`)
|
|
|
|
if (!fs.existsSync(filePath)) {
|
|
await synthesizePollyVoice(phrase, phraseId)
|
|
}
|
|
|
|
return `${process.env.NODE_ENV === "production" ? "https" : req.protocol}://${req.get("host")}${req.path}/audio/${phraseId}.mp3`
|
|
}
|
|
|
|
async function handleApiRequest(req, res) {
|
|
let { random } = req.query
|
|
|
|
// try to parse random, can be a number or a boolean
|
|
if (random) {
|
|
if (random === "true") {
|
|
random = true
|
|
} else if (Number(random)) {
|
|
random = Number(random)
|
|
}
|
|
}
|
|
|
|
const result = await getPhrases({ random })
|
|
|
|
if (random) {
|
|
const phraseId = result
|
|
.trim()
|
|
.toLowerCase()
|
|
.replace(/\s+/g, "_")
|
|
.replace(/[^\w\s]/gi, "")
|
|
|
|
return res.json({
|
|
id: phraseId,
|
|
phrase: result.trim(),
|
|
tts_file: await fetchTTSAudioURL(req, result, phraseId),
|
|
})
|
|
}
|
|
|
|
return res.json(result)
|
|
}
|
|
|
|
async function main() {
|
|
app = express()
|
|
|
|
app.use(cors())
|
|
app.use(express.json())
|
|
|
|
app.get("/api", handleApiRequest)
|
|
app.use("/api/audio", express.static(audiosPath))
|
|
|
|
app.use(express.static(path.join(__dirname, "..", "web", "dist")))
|
|
// app.get("*", (req, res) => {
|
|
// res.sendFile(path.join(__dirname, "..", "web", "dist", "index.html"))
|
|
// })
|
|
|
|
app.listen(PORT)
|
|
|
|
console.log(`Listening on port ${PORT}`)
|
|
}
|
|
|
|
main().catch(console.error)
|