feat: 功能优化
This commit is contained in:
@@ -22,6 +22,7 @@ export const VoiceService = {
|
||||
* @param {string} data.language - 语言(可选)
|
||||
* @param {string} data.gender - 音色类型(可选)
|
||||
* @param {string} data.note - 备注(可选)
|
||||
* @param {string} data.providerType - 供应商类型(可选):cosyvoice-阿里云,siliconflow-硅基流动
|
||||
* @returns {Promise}
|
||||
*/
|
||||
create(data) {
|
||||
@@ -88,8 +89,9 @@ export const VoiceService = {
|
||||
},
|
||||
|
||||
/**
|
||||
* 文本转语音(CosyVoice)
|
||||
* 文本转语音
|
||||
* @param {Object} data
|
||||
* @param {string} data.providerType - 供应商类型(可选):cosyvoice-阿里云,siliconflow-硅基流动
|
||||
* @returns {Promise}
|
||||
*/
|
||||
synthesize(data) {
|
||||
@@ -99,6 +101,7 @@ export const VoiceService = {
|
||||
/**
|
||||
* 我的音色试听
|
||||
* @param {Object} data
|
||||
* @param {string} data.providerType - 供应商类型(可选):cosyvoice-阿里云,siliconflow-硅基流动
|
||||
* @returns {Promise}
|
||||
*/
|
||||
preview(data) {
|
||||
@@ -107,4 +110,3 @@ export const VoiceService = {
|
||||
}
|
||||
|
||||
export default VoiceService
|
||||
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
/**
|
||||
* TTS (Text-to-Speech) 公共Hook
|
||||
* 支持多个供应商:Qwen, Azure, AWS等
|
||||
* 支持多个供应商:CosyVoice, SiliconFlow, Azure, AWS等
|
||||
*/
|
||||
import { ref, computed } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { VoiceService } from '@/api/voice'
|
||||
import { normalizeProviderType, VOICE_PROVIDER_TYPES } from '@/config/voiceConfig'
|
||||
|
||||
// 供应商配置
|
||||
const TTS_PROVIDERS = {
|
||||
QWEN: 'qwen',
|
||||
AZURE: 'azure',
|
||||
AWS: 'aws'
|
||||
}
|
||||
// 兼容旧代码的导出
|
||||
const TTS_PROVIDERS = VOICE_PROVIDER_TYPES
|
||||
|
||||
// 默认配置
|
||||
// 供应商默认配置(使用标准化后的键名)
|
||||
const DEFAULT_CONFIG = {
|
||||
qwen: {
|
||||
cosyvoice: {
|
||||
apiEndpoint: '/api/tik/voice/tts',
|
||||
audioFormat: 'mp3',
|
||||
supportedFormats: ['mp3', 'wav']
|
||||
@@ -32,16 +29,9 @@ const DEFAULT_CONFIG = {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TTS Hook主函数
|
||||
* @param {Object} options 配置选项
|
||||
* @param {string} options.provider 供应商名称,默认'qwen'
|
||||
* @param {Object} options.customConfig 自定义配置
|
||||
* @returns {Object} TTS相关的方法和状态
|
||||
*/
|
||||
export function useTTS(options = {}) {
|
||||
const {
|
||||
provider = TTS_PROVIDERS.QWEN,
|
||||
provider = VOICE_PROVIDER_TYPES.COSYVOICE,
|
||||
customConfig = {}
|
||||
} = options
|
||||
|
||||
@@ -59,7 +49,8 @@ export function useTTS(options = {}) {
|
||||
|
||||
// 获取当前供应商配置
|
||||
const getProviderConfig = () => {
|
||||
const config = DEFAULT_CONFIG[provider] || DEFAULT_CONFIG[TTS_PROVIDERS.QWEN]
|
||||
const normalizedProvider = normalizeProviderType(provider)
|
||||
const config = DEFAULT_CONFIG[normalizedProvider] || DEFAULT_CONFIG.cosyvoice
|
||||
return { ...config, ...customConfig }
|
||||
}
|
||||
|
||||
@@ -202,7 +193,7 @@ export function useTTS(options = {}) {
|
||||
speechRate: speechRate.value || 1.0,
|
||||
audioFormat: providerConfig.audioFormat,
|
||||
timestamp: Date.now(),
|
||||
provider: provider
|
||||
providerType: normalizeProviderType(provider)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +276,7 @@ export function useTTS(options = {}) {
|
||||
voiceConfigId: params.voiceConfigId,
|
||||
speechRate: params.speechRate || speechRate.value,
|
||||
audioFormat: params.audioFormat || providerConfig.audioFormat,
|
||||
provider: provider
|
||||
providerType: normalizeProviderType(provider)
|
||||
}
|
||||
|
||||
return await VoiceService.synthesize(ttsParams)
|
||||
|
||||
61
frontend/app/web-gold/src/config/voiceConfig.js
Normal file
61
frontend/app/web-gold/src/config/voiceConfig.js
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 语音供应商统一配置
|
||||
*/
|
||||
|
||||
// 供应商类型枚举
|
||||
export const VOICE_PROVIDER_TYPES = {
|
||||
COSYVOICE: 'cosyvoice',
|
||||
SILICONFLOW: 'siliconflow',
|
||||
QWEN: 'qwen',
|
||||
AZURE: 'azure',
|
||||
AWS: 'aws'
|
||||
}
|
||||
|
||||
// 默认供应商
|
||||
export const DEFAULT_VOICE_PROVIDER = VOICE_PROVIDER_TYPES.COSYVOICE
|
||||
|
||||
// 供应商选项(用于下拉选择)
|
||||
export const VOICE_PROVIDER_OPTIONS = [
|
||||
{ label: '阿里云 CosyVoice', value: VOICE_PROVIDER_TYPES.COSYVOICE },
|
||||
{ label: '硅基流动 SiliconFlow', value: VOICE_PROVIDER_TYPES.SILICONFLOW }
|
||||
]
|
||||
|
||||
// 供应商别名映射(兼容旧名称)
|
||||
export const PROVIDER_ALIAS_MAP = {
|
||||
[VOICE_PROVIDER_TYPES.QWEN]: VOICE_PROVIDER_TYPES.COSYVOICE
|
||||
}
|
||||
|
||||
/**
|
||||
* 标准化供应商类型(处理别名映射)
|
||||
*/
|
||||
export function normalizeProviderType(providerType) {
|
||||
if (!providerType) return DEFAULT_VOICE_PROVIDER
|
||||
return PROVIDER_ALIAS_MAP[providerType] || providerType
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取供应商显示名称
|
||||
*/
|
||||
export function getProviderLabel(providerType) {
|
||||
const option = VOICE_PROVIDER_OPTIONS.find(opt => opt.value === providerType)
|
||||
return option?.label || providerType
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查供应商是否支持
|
||||
*/
|
||||
export function isProviderSupported(providerType) {
|
||||
const normalized = normalizeProviderType(providerType)
|
||||
return Object.values(VOICE_PROVIDER_TYPES).includes(normalized)
|
||||
}
|
||||
|
||||
// 默认导出配置对象
|
||||
export default {
|
||||
VOICE_PROVIDER_TYPES,
|
||||
DEFAULT_VOICE_PROVIDER,
|
||||
VOICE_PROVIDER_OPTIONS,
|
||||
PROVIDER_ALIAS_MAP,
|
||||
normalizeProviderType,
|
||||
getProviderLabel,
|
||||
isProviderSupported
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import { MaterialService } from '@/api/material'
|
||||
import { createDigitalHumanTask, getDigitalHumanTask, cancelTask, retryTask } from '@/api/digitalHuman'
|
||||
import { extractVideoCover } from '@/utils/video-cover'
|
||||
import { useUpload } from '@/composables/useUpload'
|
||||
import { DEFAULT_VOICE_PROVIDER } from '@/config/voiceConfig'
|
||||
|
||||
// 导入 voiceStore 用于获取用户音色
|
||||
import { useVoiceCopyStore } from '@/stores/voiceCopy'
|
||||
@@ -249,41 +250,38 @@ const resetPreviewState = () => {
|
||||
}
|
||||
|
||||
const buildPreviewParams = (voice) => {
|
||||
// 公共参数
|
||||
const baseParams = {
|
||||
inputText: ttsText.value,
|
||||
speechRate: speechRate.value || 1.0,
|
||||
audioFormat: 'mp3',
|
||||
timestamp: Date.now(),
|
||||
providerType: DEFAULT_VOICE_PROVIDER
|
||||
}
|
||||
|
||||
if (voice.source === 'user') {
|
||||
// 用户音色:使用voiceConfigId,不传instruction
|
||||
// 用户音色:使用voiceConfigId
|
||||
const configId = voice.rawId || extractIdFromString(voice.id)
|
||||
if (!configId) {
|
||||
message.error('配音配置无效')
|
||||
return null
|
||||
}
|
||||
return {
|
||||
voiceConfigId: configId,
|
||||
inputText: ttsText.value,
|
||||
speechRate: speechRate.value || 1.0,
|
||||
audioFormat: 'mp3',
|
||||
timestamp: Date.now() // 添加时间戳确保每次请求不同
|
||||
}
|
||||
} else {
|
||||
// 系统音色:根据是否选择instruction或emotion来决定传递哪个参数
|
||||
const params = {
|
||||
voiceId: voice.voiceId,
|
||||
inputText: ttsText.value,
|
||||
speechRate: speechRate.value || 1.0,
|
||||
audioFormat: 'mp3',
|
||||
timestamp: Date.now() // 添加时间戳确保每次请求不同
|
||||
}
|
||||
|
||||
// instruction和emotion只能选一个传递
|
||||
if (instruction.value && instruction.value !== 'neutral') {
|
||||
params.instruction = instruction.value
|
||||
} else if (emotion.value && emotion.value !== 'neutral') {
|
||||
params.emotion = emotion.value
|
||||
} else if (voice.defaultInstruction) {
|
||||
params.instruction = voice.defaultInstruction
|
||||
}
|
||||
|
||||
return params
|
||||
return { ...baseParams, voiceConfigId: configId }
|
||||
}
|
||||
|
||||
// 系统音色:使用voiceId,可能包含instruction/emotion
|
||||
const params = { ...baseParams, voiceId: voice.voiceId }
|
||||
|
||||
// instruction和emotion只能选一个传递
|
||||
if (instruction.value && instruction.value !== 'neutral') {
|
||||
params.instruction = instruction.value
|
||||
} else if (emotion.value && emotion.value !== 'neutral') {
|
||||
params.emotion = emotion.value
|
||||
} else if (voice.defaultInstruction) {
|
||||
params.instruction = voice.defaultInstruction
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
const extractIdFromString = (idStr) => {
|
||||
@@ -303,7 +301,8 @@ const handleSynthesizeVoice = async () => {
|
||||
const params = {
|
||||
inputText: ttsText.value,
|
||||
speechRate: speechRate.value,
|
||||
audioFormat: 'mp3'
|
||||
audioFormat: 'mp3',
|
||||
providerType: DEFAULT_VOICE_PROVIDER
|
||||
}
|
||||
|
||||
if (voice.source === 'user') {
|
||||
|
||||
@@ -113,8 +113,11 @@ import { MaterialService } from '@/api/material'
|
||||
import { useUpload } from '@/composables/useUpload'
|
||||
import dayjs from 'dayjs'
|
||||
import BasicLayout from '@/layouts/components/BasicLayout.vue'
|
||||
import { VOICE_PROVIDER_OPTIONS, DEFAULT_VOICE_PROVIDER } from '@/config/voiceConfig'
|
||||
|
||||
// ========== 常量 ==========
|
||||
const PROVIDER_OPTIONS = VOICE_PROVIDER_OPTIONS
|
||||
|
||||
const DEFAULT_FORM_DATA = {
|
||||
id: null,
|
||||
name: '',
|
||||
@@ -122,7 +125,8 @@ const DEFAULT_FORM_DATA = {
|
||||
autoTranscribe: true,
|
||||
language: 'zh-CN',
|
||||
gender: 'female',
|
||||
note: ''
|
||||
note: '',
|
||||
providerType: DEFAULT_VOICE_PROVIDER
|
||||
}
|
||||
|
||||
// ========== 响应式数据 ==========
|
||||
@@ -183,7 +187,8 @@ const fillFormData = (data) => {
|
||||
fileId: data.fileId || null,
|
||||
language: data.language || 'zh-CN',
|
||||
gender: data.gender || 'female',
|
||||
note: data.note || ''
|
||||
note: data.note || '',
|
||||
providerType: data.providerType || DEFAULT_VOICE_PROVIDER
|
||||
})
|
||||
}
|
||||
|
||||
@@ -363,7 +368,8 @@ const handleSubmit = async () => {
|
||||
autoTranscribe: formData.autoTranscribe,
|
||||
language: formData.language,
|
||||
gender: formData.gender,
|
||||
note: formData.note
|
||||
note: formData.note,
|
||||
providerType: formData.providerType
|
||||
}
|
||||
: {
|
||||
id: formData.id,
|
||||
|
||||
@@ -13,6 +13,7 @@ import type {
|
||||
} from '../types/identify-face'
|
||||
// @ts-ignore
|
||||
import { VoiceService } from '@/api/voice'
|
||||
import { DEFAULT_VOICE_PROVIDER } from '@/config/voiceConfig'
|
||||
|
||||
/**
|
||||
* 语音生成 Hook
|
||||
@@ -76,6 +77,7 @@ export function useVoiceGeneration(): UseVoiceGeneration {
|
||||
voiceConfigId: voice.rawId || extractIdFromString(voice.id),
|
||||
speechRate: speechRate.value || 1.0,
|
||||
audioFormat: 'mp3' as const,
|
||||
providerType: DEFAULT_VOICE_PROVIDER,
|
||||
}
|
||||
|
||||
const res = await VoiceService.synthesize(params)
|
||||
|
||||
Reference in New Issue
Block a user