feat: 功能优化

This commit is contained in:
2026-01-27 01:39:08 +08:00
parent bf12e70339
commit 24f66c8e81
24 changed files with 1570 additions and 133 deletions

View File

@@ -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

View File

@@ -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)

View 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
}

View File

@@ -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') {

View File

@@ -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,

View File

@@ -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)