Stop throwing error and return empty speech if there is no content in the article

This commit is contained in:
Hongbo Wu 2022-09-19 17:36:58 +08:00
parent d893978567
commit b995dc0500
2 changed files with 97 additions and 75 deletions

View file

@ -94,6 +94,9 @@ export function articleRouter() {
return res.status(401).send({ errorCode: 'UNAUTHORIZED' })
}
const { uid } = jwt.decode(token) as Claims
if (!uid) {
return res.status(401).send({ errorCode: 'UNAUTHORIZED' })
}
logger.info(`Get article speech in ${outputFormat} format`, {
params: req.params,
labels: {
@ -102,84 +105,89 @@ export function articleRouter() {
},
})
if (outputFormat === 'speech') {
try {
if (outputFormat === 'speech') {
const page = await getPageById(articleId)
if (!page) {
return res.status(404).send('Page not found')
}
const speechFile = htmlToSpeechFile({
title: page.title,
content: page.content,
options: {
primaryVoice: voice,
secondaryVoice: secondaryVoice,
language: page.language,
},
})
return res.send({ ...speechFile, pageId: articleId })
}
const existingSpeech = await getRepository(Speech).findOne({
where: {
elasticPageId: articleId,
voice,
},
order: {
createdAt: 'DESC',
},
relations: ['user'],
})
if (existingSpeech) {
if (existingSpeech.user.id !== uid) {
logger.info('User is not allowed to access speech of the article', {
userId: uid,
articleId,
})
return res.status(401).send({ errorCode: 'UNAUTHORIZED' })
}
if (existingSpeech.state === SpeechState.COMPLETED) {
logger.info('Found existing completed speech', {
audioUrl: existingSpeech.audioFileName,
speechMarksUrl: existingSpeech.speechMarksFileName,
})
await updatePage(
existingSpeech.elasticPageId,
{
listenedAt: new Date(),
},
{ uid, pubsub: createPubSubClient() }
)
return res.redirect(await redirectUrl(existingSpeech, outputFormat))
}
if (existingSpeech.state === SpeechState.INITIALIZED) {
logger.info('Found existing in progress speech')
// retry later
return res.status(202).send('Speech is in progress')
}
}
logger.info('Create Text to speech task', { articleId })
const page = await getPageById(articleId)
if (!page) {
return res.status(404).send('Page not found')
}
const speechFile = htmlToSpeechFile({
title: page.title,
content: page.content,
options: {
primaryVoice: voice,
secondaryVoice: secondaryVoice,
language: page.language,
},
})
return res.send({ ...speechFile, pageId: articleId })
}
const existingSpeech = await getRepository(Speech).findOne({
where: {
// initialize state
const speech = await getRepository(Speech).save({
user: { id: uid },
elasticPageId: articleId,
state: SpeechState.INITIALIZED,
voice,
},
order: {
createdAt: 'DESC',
},
relations: ['user'],
})
if (existingSpeech) {
if (existingSpeech.user.id !== uid) {
logger.info('User is not allowed to access speech of the article', {
userId: uid,
articleId,
})
return res.status(401).send({ errorCode: 'UNAUTHORIZED' })
}
if (existingSpeech.state === SpeechState.COMPLETED) {
logger.info('Found existing completed speech', {
audioUrl: existingSpeech.audioFileName,
speechMarksUrl: existingSpeech.speechMarksFileName,
})
await updatePage(
existingSpeech.elasticPageId,
{
listenedAt: new Date(),
},
{ uid, pubsub: createPubSubClient() }
)
return res.redirect(await redirectUrl(existingSpeech, outputFormat))
}
if (existingSpeech.state === SpeechState.INITIALIZED) {
logger.info('Found existing in progress speech')
// retry later
return res.status(202).send('Speech is in progress')
}
})
// enqueue a task to convert text to speech
const taskName = await enqueueTextToSpeech({
userId: uid,
speechId: speech.id,
text: page.content,
voice: speech.voice,
priority: priority || 'high',
})
logger.info('Start Text to speech task', { taskName })
res.status(202).send('Text to speech task started')
} catch (error) {
logger.error('Error getting article speech:', error)
res.status(500).send({ errorCode: 'INTERNAL_ERROR' })
}
logger.info('Create Text to speech task', { articleId })
const page = await getPageById(articleId)
if (!page) {
return res.status(404).send('Page not found')
}
// initialize state
const speech = await getRepository(Speech).save({
user: { id: uid },
elasticPageId: articleId,
state: SpeechState.INITIALIZED,
voice,
})
// enqueue a task to convert text to speech
const taskName = await enqueueTextToSpeech({
userId: uid,
speechId: speech.id,
text: page.content,
voice: speech.voice,
priority: priority || 'high',
})
logger.info('Start Text to speech task', { taskName })
res.status(202).send('Text to speech task started')
}
)

View file

@ -303,16 +303,30 @@ const textToUtterance = ({
export const htmlToSpeechFile = (htmlInput: HtmlInput): SpeechFile => {
const { title, content, options } = htmlInput
console.log('creating speech file with options:', options)
const language = options.language || DEFAULT_LANGUAGE
const defaultVoice = options.primaryVoice || DEFAULT_VOICE
const dom = parseHTML(content)
const body = dom.document.querySelector('#readability-page-1')
if (!body) {
throw new Error('Unable to parse HTML document')
console.log('No HTML body found:', content)
return {
wordCount: 0,
language,
defaultVoice,
utterances: [],
}
}
const parsedNodes = parseDomTree(body)
if (parsedNodes.length < 1) {
throw new Error('No HTML nodes found')
console.log('No HTML nodes found:', body)
return {
wordCount: 0,
language,
defaultVoice,
utterances: [],
}
}
const tokenizer = new WordPunctTokenizer()
@ -354,8 +368,8 @@ export const htmlToSpeechFile = (htmlInput: HtmlInput): SpeechFile => {
return {
wordCount: wordOffset,
language: options.language || DEFAULT_LANGUAGE,
defaultVoice: options.primaryVoice || DEFAULT_VOICE,
language,
defaultVoice,
utterances,
}
}