mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
commit
3d11e9df8e
6 changed files with 13 additions and 19 deletions
|
|
@ -29,7 +29,7 @@ interface SpeechInput {
|
|||
secondaryVoice?: string
|
||||
priority?: 'low' | 'high'
|
||||
}
|
||||
const outputFormats = ['mp3', 'speech-marks', 'speech-file']
|
||||
const outputFormats = ['mp3', 'speech-marks', 'speech']
|
||||
const logger = buildLogger('app.dispatch')
|
||||
|
||||
export function articleRouter() {
|
||||
|
|
@ -102,7 +102,7 @@ export function articleRouter() {
|
|||
},
|
||||
})
|
||||
|
||||
if (outputFormat === 'speech-file') {
|
||||
if (outputFormat === 'speech') {
|
||||
const page = await getPageById(articleId)
|
||||
if (!page) {
|
||||
return res.status(404).send('Page not found')
|
||||
|
|
@ -112,7 +112,7 @@ export function articleRouter() {
|
|||
secondaryVoice: secondaryVoice,
|
||||
language: page.language,
|
||||
})
|
||||
return res.send(speechFile)
|
||||
return res.send({ ...speechFile, pageId: articleId })
|
||||
}
|
||||
|
||||
const existingSpeech = await getRepository(Speech).findOne({
|
||||
|
|
|
|||
3
packages/api/src/textToSpeech.d.ts
vendored
3
packages/api/src/textToSpeech.d.ts
vendored
|
|
@ -7,7 +7,7 @@ declare module '@omnivore/text-to-speech-handler' {
|
|||
export interface SSMLOptions {
|
||||
primaryVoice?: string
|
||||
secondaryVoice?: string
|
||||
rate?: number
|
||||
rate?: string
|
||||
language?: string
|
||||
}
|
||||
|
||||
|
|
@ -21,7 +21,6 @@ declare module '@omnivore/text-to-speech-handler' {
|
|||
|
||||
export interface SpeechFile {
|
||||
wordCount: number
|
||||
averageWPM: number
|
||||
language: string
|
||||
defaultVoice: string
|
||||
utterances: Utterance[]
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ export interface Utterance {
|
|||
|
||||
export interface SpeechFile {
|
||||
wordCount: number
|
||||
averageWPM: number
|
||||
language: string
|
||||
defaultVoice: string
|
||||
utterances: Utterance[]
|
||||
|
|
@ -33,14 +32,13 @@ export type SSMLItem = {
|
|||
export type SSMLOptions = {
|
||||
primaryVoice?: string
|
||||
secondaryVoice?: string
|
||||
rate?: number
|
||||
rate?: string
|
||||
language?: string
|
||||
}
|
||||
|
||||
const WORDS_PER_MINUTE = 200
|
||||
const DEFAULT_LANGUAGE = 'en-US'
|
||||
const DEFAULT_VOICE = 'en-US-JennyNeural'
|
||||
const DEFAULT_RATE = 1.25
|
||||
const DEFAULT_RATE = '1.0'
|
||||
|
||||
const ANCHOR_ELEMENTS_BLOCKED_ATTRIBUTES = [
|
||||
'omnivore-highlight-id',
|
||||
|
|
@ -300,14 +298,13 @@ export const htmlToSpeechFile = (
|
|||
wordOffset,
|
||||
node.nodeName === 'BLOCKQUOTE' ? options.secondaryVoice : undefined
|
||||
)
|
||||
utterances.push(utterance)
|
||||
utterance.wordCount > 0 && utterances.push(utterance)
|
||||
wordOffset += utterance.wordCount
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
wordCount: wordOffset,
|
||||
averageWPM: WORDS_PER_MINUTE,
|
||||
language: options.language || DEFAULT_LANGUAGE,
|
||||
defaultVoice: options.primaryVoice || DEFAULT_VOICE,
|
||||
utterances,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import { htmlToSpeechFile } from './htmlToSsml'
|
|||
|
||||
interface UtteranceInput {
|
||||
voice?: string
|
||||
rate?: number
|
||||
rate?: string
|
||||
language?: string
|
||||
text: string
|
||||
idx: string
|
||||
|
|
@ -24,7 +24,7 @@ interface HTMLInput {
|
|||
text: string
|
||||
voice?: string
|
||||
language?: string
|
||||
rate?: number
|
||||
rate?: string
|
||||
complimentaryVoice?: string
|
||||
bucket: string
|
||||
}
|
||||
|
|
@ -160,9 +160,6 @@ export const textToSpeechStreamingHandler = Sentry.GCPFunction.wrapHttpFunction(
|
|||
|
||||
try {
|
||||
const utteranceInput = req.body as UtteranceInput
|
||||
if (!utteranceInput.text) {
|
||||
return res.status(400).send({ errorCode: 'INVALID_DATA' })
|
||||
}
|
||||
const input: TextToSpeechInput = {
|
||||
...utteranceInput,
|
||||
textType: 'utterance',
|
||||
|
|
|
|||
|
|
@ -8,13 +8,14 @@ import {
|
|||
SpeechSynthesizer,
|
||||
} from 'microsoft-cognitiveservices-speech-sdk'
|
||||
import { endSsml, htmlToSsmlItems, ssmlItemText, startSsml } from './htmlToSsml'
|
||||
import * as _ from 'underscore'
|
||||
|
||||
export interface TextToSpeechInput {
|
||||
text: string
|
||||
voice?: string
|
||||
language?: string
|
||||
textType?: 'html' | 'utterance'
|
||||
rate?: number
|
||||
rate?: string
|
||||
secondaryVoice?: string
|
||||
audioStream?: NodeJS.ReadWriteStream
|
||||
}
|
||||
|
|
@ -150,7 +151,7 @@ export const synthesizeTextToSpeech = async (
|
|||
// for utterance
|
||||
const start = startSsml(ssmlOptions)
|
||||
wordOffset = -start.length
|
||||
const ssml = `${start}${input.text}${endSsml()}`
|
||||
const ssml = `${start}${_.escape(input.text)}${endSsml()}`
|
||||
const result = await speakSsmlAsyncPromise(ssml)
|
||||
return {
|
||||
audioData: Buffer.from(result.audioData),
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ describe('htmlToSsmlItems', () => {
|
|||
primaryVoice: 'test-primary',
|
||||
secondaryVoice: 'test-secondary',
|
||||
language: 'en-US',
|
||||
rate: 1,
|
||||
rate: '1.0',
|
||||
}
|
||||
|
||||
describe('a simple html file', () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue