Upload speech marks file to gcs too

This commit is contained in:
Hongbo Wu 2022-08-17 11:24:53 +08:00
parent c482ad320b
commit 02ba753375
4 changed files with 48 additions and 13 deletions

View file

@ -25,7 +25,7 @@ export class Speech {
audioUrl!: string
@Column('text')
speechMarks!: string
speechMarksUrl!: string
@Column('text')
voice!: string

View file

@ -72,10 +72,11 @@ export function articleRouter() {
})
router.get(
'/:id/mp3',
'/:id/:outputFormat',
cors<express.Request>(corsConfig),
async (req, res) => {
const id = req.params.id
const outputFormat = req.params.outputFormat
const token = req.cookies?.auth || req.headers?.authorization
if (!token || !jwt.verify(token, env.server.jwtSecret)) {
return res.status(401).send({ errorCode: 'UNAUTHORIZED' })
@ -83,15 +84,27 @@ export function articleRouter() {
const { uid } = jwt.decode(token) as Claims
const startTime = Date.now()
logger.info('Get article speech in mp3 format', {
logger.info(`Get article speech in ${outputFormat} format`, {
params: req.params,
labels: {
userId: uid,
source: 'GetArticleSpeechMp3',
articleId: id,
outputFormat,
},
})
const existingSpeech = await getRepository(Speech).findOneBy({
elasticPageId: id,
})
if (existingSpeech) {
logger.info('Found existing speech', {
audioUrl: existingSpeech.audioUrl,
speechMarksUrl: existingSpeech.speechMarksUrl,
})
return res.redirect(redirectUrl(existingSpeech, outputFormat))
}
logger.debug('Text to speech request', { articleId: id })
const userPersonalization = await getRepository(
UserPersonalization
@ -112,27 +125,39 @@ export function articleRouter() {
return res.status(200).send('Page has no text')
}
const speech = await synthesizeTextToSpeech({
const speechOutput = await synthesizeTextToSpeech({
id,
text,
languageCode: page.language,
voice: userPersonalization.speechVoice,
})
await getRepository(Speech).save({
const speech = await getRepository(Speech).save({
elasticPageId: id,
audioUrl: speech.audioUrl,
speechMarks: JSON.stringify(speech.speechMarks),
audioUrl: speechOutput.audioUrl,
speechMarksUrl: speechOutput.speechMarksUrl,
user: { id: uid },
})
logger.info('Found speech mp3', {
logger.info('Created speech', {
audioUrl: speech.audioUrl,
speechMarksUrl: speech.speechMarksUrl,
duration: Date.now() - startTime,
})
res.redirect(speech.audioUrl)
res.redirect(redirectUrl(speech, outputFormat))
}
)
return router
}
const redirectUrl = (speech: Speech, outputFormat: string) => {
switch (outputFormat) {
case 'mp3':
return speech.audioUrl
case 'json':
return speech.speechMarksUrl
default:
return speech.audioUrl
}
}

View file

@ -1,5 +1,5 @@
import { buildLogger } from './logger'
import { createGCSFile, getFilePublicUrl } from './uploads'
import { createGCSFile, getFilePublicUrl, uploadToBucket } from './uploads'
import {
CancellationDetails,
CancellationReason,
@ -21,7 +21,7 @@ export interface TextToSpeechInput {
export interface TextToSpeechOutput {
audioUrl: string
speechMarks: SpeechMark[]
speechMarksUrl: string
}
export interface SpeechMark {
@ -142,9 +142,19 @@ export const synthesizeTextToSpeech = async (
logger.debug(`audio file: ${audioFile}`)
// upload Speech Marks file to GCS
const speechMarksFile = `speech/${input.id}.json`
await uploadToBucket(
speechMarksFile,
Buffer.from(JSON.stringify(speechMarks)),
{
public: true,
}
)
return {
audioUrl: getFilePublicUrl(audioFile),
speechMarks,
speechMarksUrl: getFilePublicUrl(speechMarksFile),
}
}

View file

@ -10,7 +10,7 @@ CREATE TABLE omnivore.speech (
elastic_page_id TEXT NOT NULL,
voice text,
audio_url text NOT NULL,
speech_marks text NOT NULL,
speech_marks_url text NOT NULL,
created_at timestamptz NOT NULL DEFAULT current_timestamp,
updated_at timestamptz NOT NULL DEFAULT current_timestamp
);