fix(viral-video): 预设音色ID与后端对齐(longxiaochun_v3等v3后缀)+ 取消模糊匹配兜底 #2130

Merged
xiaoxia merged 1 commits from fix/2041-preset-voice-ids into develop 2026-10-01 22:42:17 +08:00
@@ -34,83 +34,63 @@ interface Props {
/** 兜底 mock 音色(后端 /api/v1/tts/presets 返回字段不够时使用) */
const MOCK_VOICES: PresetVoice[] = [
// ⚠️ 兜底 mock,仅在 /voices/presets 接口不可达时使用;ID 必须与后端
// packages/domain/preset_voices.py PRESET_VOICES 的 voice_id 对齐(v3后缀)
{
id: "long-xiaochun",
id: "longxiaochun_v3",
name: "龙小淳",
gender: "female",
category: "女声",
desc: "知性积极女声,适合语音助手",
},
{
id: "long-xiaoxia",
id: "longxiaoxia_v3",
name: "龙小夏",
gender: "female",
category: "女声",
desc: "沉稳权威女声,适合新闻播报",
},
{
id: "long-xiaoyan",
name: "龙小颜",
gender: "female",
category: "女声",
desc: "温柔甜美女声,适合情感口播",
},
{
id: "long-xiaotong",
name: "龙小彤",
gender: "female",
category: "女声",
desc: "活力少女音,适合短视频带货",
},
{
id: "long-sanshu",
id: "longsanshu_v3",
name: "龙三叔",
gender: "male",
category: "男声",
desc: "沉稳质感男声,适合有声书",
},
{
id: "long-xiaogang",
name: "龙小刚",
gender: "male",
category: "男声",
desc: "阳光活力男声,适合解说",
},
{
id: "long-xiaocheng",
name: "龙小诚",
gender: "male",
category: "男声",
desc: "磁性商务男声,适合品牌宣传",
},
{
id: "long-xiaozhi",
name: "龙小智",
gender: "child",
category: "童声",
desc: "可爱童声,适合亲子内容",
},
{
id: "long-yue",
id: "longyue_v3",
name: "龙悦",
gender: "female",
category: "情绪",
desc: "温柔治愈女声,适合睡前/助眠",
category: "女声",
desc: "温暖磁性女声,适合广告配音",
},
{
id: "long-xiaodong",
name: "龙晓东",
id: "longshu_v3",
name: "龙书",
gender: "male",
category: "方言",
desc: "东北方言男声,接地气",
category: "男声",
desc: "沉稳青年男声,适合教育讲解",
},
{ id: "long-xiaoling", name: "龙小玲", gender: "female", category: "方言", desc: "粤语女声" },
{
id: "long-xiaoxiao-neural",
name: "晓晓",
id: "longyingjing_v3",
name: "龙应静",
gender: "female",
category: "女声",
desc: "温柔女声",
desc: "低调冷静女声,适合纪录片解说",
},
{
id: "longshuo_v3",
name: "龙硕",
gender: "male",
category: "男声",
desc: "博才干练男声,适合科技类内容",
},
{
id: "longtian_v3",
name: "龙甜",
gender: "female",
category: "女声",
desc: "活泼女声,适合短视频配音",
},
]
@@ -178,23 +158,22 @@ const PresetVoicePickerModal: React.FC<Props> = ({
// 合并真实数据和 mock:如果真实数据 gender/category 缺失,用 mock 兜底
const allVoices: PresetVoice[] = useMemo(() => {
// 真实 API 返回的 voice_id 以 API 为准(如 longxiaochun_v3),前端不做硬编码覆盖
const realList: PresetVoice[] = (voices || []).map((v) => {
// 按 name 模糊匹配 mock 获取补充信息
const mockMatch = MOCK_VOICES.find(
(m) => v.name?.includes(m.name.slice(1)) || m.name.includes(v.name?.slice(0, 2) || "___"),
)
// 按 id 精确匹配 mock 获取补充元信息(id 即 voice_id,唯一稳定键)
const mockMatch = MOCK_VOICES.find((m) => m.id === v.id)
return {
...v,
gender: v.gender || mockMatch?.gender,
category:
v.category ||
mockMatch?.category ||
(v.gender === "female" ? "女声" : v.gender === "male" ? "男声" : undefined),
(v.gender === "female" ? "女声" : v.gender === "male" ? "男声" : "其他"),
desc: v.desc || mockMatch?.desc,
sample_audio_url: v.sample_audio_url,
}
})
// 如果没有真实数据,使用 mock
// 如果没有真实数据,使用兜底 mock(接口失败时)
return realList.length > 0 ? realList : MOCK_VOICES
}, [voices])