feat: 更新语音转文字功能,支持从音频链接获取音频数据并转换为base64;添加错误处理和权限验证
This commit is contained in:
@@ -1,20 +1,109 @@
|
||||
import { app } from '@/app.ts'
|
||||
import { app, oss } from '@/app.ts'
|
||||
import { asr } from './modules/index.ts'
|
||||
import z from 'zod'
|
||||
import { baseURL } from '@/modules/domain.ts'
|
||||
import { getObjectByPathname } from '@/modules/fm-manager/index.ts'
|
||||
export const createAsr = async (opts: { base64Data: string }) => {
|
||||
const { base64Data } = opts
|
||||
const result = await asr.getText({
|
||||
audio: {
|
||||
data: base64Data,
|
||||
format: 'wav' as any,
|
||||
rate: 16000,
|
||||
channel: 1
|
||||
},
|
||||
request: {
|
||||
enable_words: true,
|
||||
enable_sentence_info: true,
|
||||
enable_utterance_info: true,
|
||||
enable_punctuation_prediction: true,
|
||||
enable_inverse_text_normalization: true
|
||||
}
|
||||
})
|
||||
return {
|
||||
text: result.result?.text || '',
|
||||
result
|
||||
};
|
||||
}
|
||||
app.route({
|
||||
path: 'asr',
|
||||
key: 'text',
|
||||
middleware: ['auth'],
|
||||
description: '语音转文字,将base64的音频数据转换为文字, 参数: base64Audio, 为base64编码的音频数据',
|
||||
}).define(async (ctx) => {
|
||||
const base64Audio = ctx.query.base64Audio as string
|
||||
if (!base64Audio) {
|
||||
ctx.throw('Missing base64Audio parameter')
|
||||
}
|
||||
const result = await asr.getText({
|
||||
audio: {
|
||||
data: base64Audio
|
||||
description: '语音转文字,将base64的音频数据转换为文字, 参数: base64Data 为base64编码的音频数据',
|
||||
metadata: {
|
||||
args: {
|
||||
base64Data: z.string().describe('base64编码的音频数据').nonempty('base64Data参数不能为空'),
|
||||
}
|
||||
})
|
||||
ctx.body = result
|
||||
}
|
||||
}).define(async (ctx) => {
|
||||
const base64Data = ctx.query.base64Data as string
|
||||
if (!base64Data) {
|
||||
ctx.throw(400, 'base64Data参数不能为空')
|
||||
}
|
||||
const result = await createAsr({ base64Data })
|
||||
ctx.body = {
|
||||
text: result.text
|
||||
}
|
||||
})
|
||||
.addTo(app)
|
||||
|
||||
app.route({
|
||||
path: 'asr',
|
||||
key: 'link',
|
||||
middleware: ['auth'],
|
||||
description: '语音转文字,将音频链接的音频数据转换为文字, 参数: url 为音频链接',
|
||||
metadata: {
|
||||
args: {
|
||||
url: z.string().describe('音频链接').nonempty('url参数不能为空'),
|
||||
}
|
||||
}
|
||||
}).define(async (ctx) => {
|
||||
const tokenUser = ctx.state.tokenUser;
|
||||
const url = ctx.query.url as string
|
||||
if (!url) {
|
||||
ctx.throw(400, 'url参数不能为空')
|
||||
}
|
||||
let base64Data: string = null;
|
||||
if (url.startsWith(baseURL) || url.startsWith('/')) {
|
||||
const pathname = new URL(url, baseURL).pathname;
|
||||
const [username] = pathname.split('/').filter(Boolean)
|
||||
if (username !== tokenUser.username) {
|
||||
ctx.throw(403, '没有权限访问该音频链接')
|
||||
}
|
||||
let data: Awaited<ReturnType<typeof oss.getObject>>;
|
||||
try {
|
||||
console.log('fetch audio from minio with objectName', pathname.slice(1))
|
||||
const objectName = getObjectByPathname({ pathname })
|
||||
data = await oss.getObject(objectName.objectName)
|
||||
} catch (e: any) {
|
||||
if (e?.name === 'NoSuchKey' || e?.$metadata?.httpStatusCode === 404) {
|
||||
ctx.throw(404, '音频文件不存在')
|
||||
}
|
||||
}
|
||||
if (!data.Body) {
|
||||
ctx.throw(404, '音频文件内容为空')
|
||||
}
|
||||
const bytes = await data.Body.transformToByteArray()
|
||||
base64Data = Buffer.from(bytes).toString('base64')
|
||||
} else if (url.startsWith('http')) {
|
||||
base64Data = await fetchAudioAsBase64(url)
|
||||
} else {
|
||||
ctx.throw(400, 'url参数必须是有效的链接')
|
||||
}
|
||||
// 这里需要将音频链接转换为base64数据,可以使用fetch获取音频数据并转换为base64
|
||||
const result = await createAsr({ base64Data })
|
||||
ctx.body = {
|
||||
text: result.text
|
||||
}
|
||||
})
|
||||
.addTo(app)
|
||||
|
||||
const fetchAudioAsBase64 = async (url: string): Promise<string> => {
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch audio from URL: ${response.statusText}`);
|
||||
}
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
const base64String = btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)));
|
||||
return base64String;
|
||||
};
|
||||
@@ -4,4 +4,5 @@ import { auraConfig } from '../../config.ts'
|
||||
export const asr = new Asr({
|
||||
appid: auraConfig.VOLCENGINE_AUC_APPID,
|
||||
token: auraConfig.VOLCENGINE_AUC_TOKEN,
|
||||
type: 'flash'
|
||||
})
|
||||
Reference in New Issue
Block a user