'commit'
This commit is contained in:
Binary file not shown.
@@ -2,7 +2,7 @@ import logging
|
|||||||
import uuid
|
import uuid
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException
|
from fastapi import APIRouter, HTTPException, Query # 添加Query导入
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from Util.GengerateAudio import ByteDanceTTS
|
from Util.GengerateAudio import ByteDanceTTS
|
||||||
@@ -36,17 +36,13 @@ class TextToSpeechResponse(BaseModel):
|
|||||||
request_id: Optional[str] = None
|
request_id: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
@router.get("/voices/categories")
|
@router.get("/voice-categories")
|
||||||
async def get_voice_categories():
|
async def get_voice_categories():
|
||||||
"""
|
|
||||||
获取所有音色分类接口
|
|
||||||
返回所有可用的音色分类列表
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
categories = tts_instance.get_all_categories()
|
categories = tts_instance.get_all_categories()
|
||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
"data": categories,
|
"data": categories, # 恢复为原始的 data 字段
|
||||||
"message": "获取音色分类成功"
|
"message": "获取音色分类成功"
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -56,15 +52,9 @@ async def get_voice_categories():
|
|||||||
detail=f"获取音色分类失败: {str(e)}"
|
detail=f"获取音色分类失败: {str(e)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 恢复原始的音色列表接口路由
|
||||||
@router.get("/voices/by-category/{category}")
|
@router.get("/voices")
|
||||||
async def get_voices_by_category(category: str):
|
async def get_voices_by_category(category: str = Query(...)): # 现在Query已定义
|
||||||
"""
|
|
||||||
根据分类获取音色列表接口
|
|
||||||
Args:
|
|
||||||
category: 音色分类名称
|
|
||||||
返回指定分类下的所有音色列表
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
voices = tts_instance.get_voices_by_category(category)
|
voices = tts_instance.get_voices_by_category(category)
|
||||||
if not voices:
|
if not voices:
|
||||||
@@ -86,7 +76,7 @@ async def get_voices_by_category(category: str):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/voices/all")
|
@router.get("/all")
|
||||||
async def get_all_voices():
|
async def get_all_voices():
|
||||||
"""
|
"""
|
||||||
获取所有音色分类和音色列表接口
|
获取所有音色分类和音色列表接口
|
||||||
|
@@ -26,44 +26,78 @@ class ByteDanceTTS:
|
|||||||
# 音色分类字典
|
# 音色分类字典
|
||||||
TTS_VOICES = {
|
TTS_VOICES = {
|
||||||
"通用场景": {
|
"通用场景": {
|
||||||
"zh_female_xiaoxue_moon_bigtts": "小雪(女声,温柔亲切)",
|
"BV700_V2_streaming": "灿灿 2.0",
|
||||||
"zh_male_xiaofeng_common": "小峰(男声,沉稳大气)",
|
"BV705_streaming": "炀炀",
|
||||||
"zh_female_xiaoxin_common": "小新(女声,自然流畅)",
|
"BV701_V2_streaming": "擎苍 2.0",
|
||||||
"zh_male_xiaoyu_common": "小鱼(男声,年轻活力)"
|
"BV001_V2_streaming": "通用女声 2.0",
|
||||||
|
"BV700_streaming": "灿灿",
|
||||||
|
"BV406_V2_streaming": "超自然音色-梓梓2.0",
|
||||||
|
"BV406_streaming": "超自然音色-梓梓",
|
||||||
|
"BV407_V2_streaming": "超自然音色-燃燃2.0",
|
||||||
|
"BV407_streaming": "超自然音色-燃燃",
|
||||||
|
"BV001_streaming": "通用女声(12种情感)",
|
||||||
|
"BV002_streaming": "通用男声"
|
||||||
},
|
},
|
||||||
"有声阅读": {
|
"有声阅读": {
|
||||||
"zh_female_xiaoxue_moon_bigtts": "小雪(女声,温柔亲切)",
|
"BV701_streaming": "擎苍",
|
||||||
"zh_female_xiaoxin_common": "小新(女声,自然流畅)",
|
"BV123_streaming": "阳光青年",
|
||||||
"zh_female_xiaomei_moon_bigtts": "小美(女声,甜美温柔)",
|
"BV120_streaming": "反卷青年",
|
||||||
"zh_female_xiaoli_moon_bigtts": "小丽(女声,清晰标准)"
|
"BV119_streaming": "通用赘婿",
|
||||||
|
"BV115_streaming": "古风少御",
|
||||||
|
"BV107_streaming": "霸气青叔",
|
||||||
|
"BV100_streaming": "质朴青年",
|
||||||
|
"BV104_streaming": "温柔淑女",
|
||||||
|
"BV004_streaming": "开朗青年",
|
||||||
|
"BV113_streaming": "甜宠少御",
|
||||||
|
"BV102_streaming": "儒雅青年"
|
||||||
},
|
},
|
||||||
"智能助手": {
|
"智能助手": {
|
||||||
"zh_female_xiaoxue_moon_bigtts": "小雪(女声,温柔亲切)",
|
"BV405_streaming": "甜美小源",
|
||||||
"zh_male_xiaofeng_common": "小峰(男声,沉稳大气)",
|
"BV007_streaming": "亲切女声",
|
||||||
"zh_female_xiaoxin_common": "小新(女声,自然流畅)",
|
"BV009_streaming": "知性女声",
|
||||||
"zh_male_xiaoyu_common": "小鱼(男声,年轻活力)"
|
"BV419_streaming": "诚诚",
|
||||||
|
"BV415_streaming": "童童",
|
||||||
|
"BV008_streaming": "亲切男声"
|
||||||
},
|
},
|
||||||
"视频配音": {
|
"视频配音": {
|
||||||
"zh_male_xiaofeng_common": "小峰(男声,沉稳大气)",
|
"BV408_streaming": "译制片男声",
|
||||||
"zh_female_xiaomei_moon_bigtts": "小美(女声,甜美温柔)",
|
"BV426_streaming": "懒小羊",
|
||||||
"zh_female_xiaoli_moon_bigtts": "小丽(女声,清晰标准)",
|
"BV428_streaming": "清新文艺女声",
|
||||||
"zh_male_xiaoyu_common": "小鱼(男声,年轻活力)"
|
"BV403_streaming": "鸡汤女声",
|
||||||
|
"BV158_streaming": "智慧老者",
|
||||||
|
"BV157_streaming": "慈爱姥姥",
|
||||||
|
"BR001_streaming": "说唱小哥",
|
||||||
|
"BV410_streaming": "活力解说男",
|
||||||
|
"BV411_streaming": "影视解说小帅",
|
||||||
|
"BV437_streaming": "解说小帅-多情感",
|
||||||
|
"BV412_streaming": "影视解说小美",
|
||||||
|
"BV159_streaming": "纨绔青年",
|
||||||
|
"BV418_streaming": "直播一姐",
|
||||||
|
"BV142_streaming": "沉稳解说男",
|
||||||
|
"BV143_streaming": "潇洒青年",
|
||||||
|
"BV056_streaming": "阳光男声",
|
||||||
|
"BV005_streaming": "活泼女声",
|
||||||
|
"BV064_streaming": "小萝莉"
|
||||||
},
|
},
|
||||||
"特色音色": {
|
"特色音色": {
|
||||||
"zh_female_xiaoxue_moon_bigtts": "小雪(女声,温柔亲切)",
|
"BV051_streaming": "奶气萌娃",
|
||||||
"zh_female_xiaomei_moon_bigtts": "小美(女声,甜美温柔)"
|
"BV063_streaming": "动漫海绵",
|
||||||
|
"BV417_streaming": "动漫海星",
|
||||||
|
"BV050_streaming": "动漫小新",
|
||||||
|
"BV061_streaming": "天才童声"
|
||||||
},
|
},
|
||||||
"广告配音": {
|
"广告配音": {
|
||||||
"zh_male_xiaofeng_common": "小峰(男声,沉稳大气)",
|
"BV401_streaming": "促销男声",
|
||||||
"zh_female_xiaoli_moon_bigtts": "小丽(女声,清晰标准)"
|
"BV402_streaming": "促销女声",
|
||||||
|
"BV006_streaming": "磁性男声"
|
||||||
},
|
},
|
||||||
"新闻播报": {
|
"新闻播报": {
|
||||||
"zh_female_xiaoli_moon_bigtts": "小丽(女声,清晰标准)",
|
"BV011_streaming": "新闻女声",
|
||||||
"zh_male_xiaofeng_common": "小峰(男声,沉稳大气)"
|
"BV012_streaming": "新闻男声"
|
||||||
},
|
},
|
||||||
"教育场景": {
|
"教育场景": {
|
||||||
"zh_female_xiaoxin_common": "小新(女声,自然流畅)",
|
"BV034_streaming": "知性姐姐-双语",
|
||||||
"zh_male_xiaoyu_common": "小鱼(男声,年轻活力)"
|
"BV033_streaming": "温柔小哥"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binary file not shown.
@@ -158,6 +158,12 @@
|
|||||||
min-height: 100px;
|
min-height: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.voice-options p {
|
||||||
|
color: #666;
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.voice-card {
|
.voice-card {
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -166,12 +172,6 @@
|
|||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.voice-options p {
|
|
||||||
color: #666;
|
|
||||||
text-align: center;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.voice-card:hover {
|
.voice-card:hover {
|
||||||
border-color: #3498db;
|
border-color: #3498db;
|
||||||
box-shadow: 0 2px 8px rgba(52, 152, 219, 0.2);
|
box-shadow: 0 2px 8px rgba(52, 152, 219, 0.2);
|
||||||
@@ -242,7 +242,7 @@
|
|||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="text-input">输入文本</label>
|
<label for="text-input">输入文本</label>
|
||||||
<textarea id="text-input" placeholder="请输入要转换为语音的文本..."></textarea>
|
<textarea id="text-input" placeholder="请输入要转换为语音的文本...">海上升明月,天涯共此时。</textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -321,105 +321,121 @@
|
|||||||
// 当前选中的音色
|
// 当前选中的音色
|
||||||
let selectedVoiceType = null;
|
let selectedVoiceType = null;
|
||||||
|
|
||||||
// API基础URL
|
// API基础URL - 使用完整URL
|
||||||
const apiBaseUrl = '/api/VideoRetalk';
|
const apiBaseUrl = window.location.origin + '/api/tts';
|
||||||
|
|
||||||
|
// 显示错误信息
|
||||||
|
function showError(message) {
|
||||||
|
errorMessage.textContent = message;
|
||||||
|
errorMessage.style.display = 'block';
|
||||||
|
successMessage.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示成功信息
|
||||||
|
function showSuccess(message) {
|
||||||
|
successMessage.textContent = message;
|
||||||
|
successMessage.style.display = 'block';
|
||||||
|
errorMessage.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐藏所有消息
|
||||||
|
function hideMessages() {
|
||||||
|
errorMessage.style.display = 'none';
|
||||||
|
successMessage.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
// 获取所有音色分类
|
// 获取所有音色分类
|
||||||
async function loadVoiceCategories() {
|
async function loadVoiceCategories() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${apiBaseUrl}/voices/categories`);
|
console.log('正在加载音色分类,API地址:', `${apiBaseUrl}/voice-categories`);
|
||||||
|
const response = await fetch(`${apiBaseUrl}/voice-categories`);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`API请求失败: ${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
console.log('音色分类数据:', data);
|
||||||
|
|
||||||
if (data.success) {
|
// 清空下拉列表
|
||||||
// 清空现有选项
|
categorySelect.innerHTML = '<option value="">请选择音色分类</option>';
|
||||||
categorySelect.innerHTML = '<option value="">请选择音色分类</option>';
|
|
||||||
|
|
||||||
// 添加分类选项
|
// 检查返回数据结构
|
||||||
|
if (data.success && data.data && Array.isArray(data.data) && data.data.length > 0) {
|
||||||
data.data.forEach(category => {
|
data.data.forEach(category => {
|
||||||
const option = document.createElement('option');
|
const option = document.createElement('option');
|
||||||
option.value = category;
|
option.value = category; // 直接使用分类名称作为值
|
||||||
option.textContent = category;
|
option.textContent = category;
|
||||||
categorySelect.appendChild(option);
|
categorySelect.appendChild(option);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
showError('获取音色分类失败: ' + data.message);
|
showError('未能加载音色分类列表');
|
||||||
|
console.error('音色分类数据格式不正确:', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定分类变化事件(确保只绑定一次)
|
||||||
|
if (!categorySelect.dataset.eventBound) {
|
||||||
|
categorySelect.addEventListener('change', function() {
|
||||||
|
if (this.value) {
|
||||||
|
loadVoicesByCategory(this.value);
|
||||||
|
} else {
|
||||||
|
voiceOptions.innerHTML = '<p>请先选择音色分类</p>';
|
||||||
|
selectedVoiceType = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
categorySelect.dataset.eventBound = 'true';
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showError('获取音色分类失败: ' + error.message);
|
console.error('加载音色分类失败:', error);
|
||||||
|
showError(`加载音色分类失败: ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据分类获取音色列表
|
// 加载指定分类的音色
|
||||||
async function loadVoicesByCategory(category) {
|
async function loadVoicesByCategory(categoryId) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${apiBaseUrl}/voices/by-category/${category}`);
|
console.log('正在加载音色列表,分类:', categoryId);
|
||||||
|
const response = await fetch(`${apiBaseUrl}/voices?category=${encodeURIComponent(categoryId)}`);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`API请求失败: ${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
console.log('音色列表数据:', data);
|
||||||
|
voiceOptions.innerHTML = '';
|
||||||
|
|
||||||
if (data.success) {
|
// 检查返回数据结构
|
||||||
// 清空现有音色选项
|
if (data.success && data.data && typeof data.data === 'object' && Object.keys(data.data).length > 0) {
|
||||||
voiceOptions.innerHTML = '';
|
Object.entries(data.data).forEach(([voiceId, voiceName]) => {
|
||||||
|
|
||||||
// 添加数据类型检查
|
|
||||||
if (typeof data.data !== 'object' || data.data === null) {
|
|
||||||
showError('获取的音色列表格式不正确');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将对象转换为数组格式 [{voice_type, name, description}, ...]
|
|
||||||
const voicesArray = Object.entries(data.data).map(([voiceType, description]) => {
|
|
||||||
// 从描述中提取名称和说明(假设格式为 "名称(说明)")
|
|
||||||
const match = description.match(/^(.*?)\((.*?)\)$/);
|
|
||||||
return {
|
|
||||||
voice_type: voiceType,
|
|
||||||
name: match ? match[1] : description,
|
|
||||||
description: match ? match[2] : '无描述'
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// 检查数组是否为空
|
|
||||||
if (voicesArray.length === 0) {
|
|
||||||
voiceOptions.innerHTML = '<p>该分类下没有可用音色</p>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加音色卡片
|
|
||||||
voicesArray.forEach(voice => {
|
|
||||||
const voiceCard = document.createElement('div');
|
const voiceCard = document.createElement('div');
|
||||||
voiceCard.className = 'voice-card';
|
voiceCard.className = 'voice-card';
|
||||||
voiceCard.dataset.voiceType = voice.voice_type;
|
voiceCard.dataset.voiceType = voiceId;
|
||||||
|
|
||||||
const voiceName = document.createElement('div');
|
voiceCard.innerHTML = `
|
||||||
voiceName.className = 'voice-name';
|
<div class="voice-name">${voiceName}</div>
|
||||||
voiceName.textContent = voice.name;
|
`;
|
||||||
|
|
||||||
const voiceDescription = document.createElement('div');
|
|
||||||
voiceDescription.className = 'voice-description';
|
|
||||||
voiceDescription.textContent = voice.description || '暂无描述';
|
|
||||||
|
|
||||||
voiceCard.appendChild(voiceName);
|
|
||||||
voiceCard.appendChild(voiceDescription);
|
|
||||||
|
|
||||||
// 添加点击事件
|
|
||||||
voiceCard.addEventListener('click', function() {
|
voiceCard.addEventListener('click', function() {
|
||||||
// 移除其他卡片的选中状态
|
// 移除其他选中状态
|
||||||
document.querySelectorAll('.voice-card').forEach(card => {
|
document.querySelectorAll('.voice-card').forEach(card => {
|
||||||
card.classList.remove('selected');
|
card.classList.remove('selected');
|
||||||
});
|
});
|
||||||
|
// 设置当前选中状态
|
||||||
// 添加当前卡片的选中状态
|
|
||||||
this.classList.add('selected');
|
this.classList.add('selected');
|
||||||
|
|
||||||
// 保存选中的音色类型
|
|
||||||
selectedVoiceType = this.dataset.voiceType;
|
selectedVoiceType = this.dataset.voiceType;
|
||||||
|
console.log('已选择音色:', selectedVoiceType);
|
||||||
});
|
});
|
||||||
|
|
||||||
voiceOptions.appendChild(voiceCard);
|
voiceOptions.appendChild(voiceCard);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
voiceOptions.innerHTML = '<p>获取音色列表失败: ' + data.message + '</p>';
|
voiceOptions.innerHTML = '<p>该分类下没有可用音色</p>';
|
||||||
|
console.error('音色列表数据格式不正确:', data);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
voiceOptions.innerHTML = '<p>获取音色列表失败: ' + error.message + '</p>';
|
console.error('加载音色列表失败:', error);
|
||||||
|
showError('加载音色列表失败,请重试');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,8 +472,10 @@
|
|||||||
encoding: 'mp3'
|
encoding: 'mp3'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log('正在生成音频,请求数据:', requestData);
|
||||||
|
|
||||||
// 发送请求
|
// 发送请求
|
||||||
const response = await fetch(`${apiBaseUrl}/tts`, {
|
const response = await fetch(`${apiBaseUrl}/generate`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
@@ -466,6 +484,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
console.log('音频生成结果:', data);
|
||||||
|
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
// 显示成功消息
|
// 显示成功消息
|
||||||
@@ -477,61 +496,50 @@
|
|||||||
|
|
||||||
// 设置下载按钮
|
// 设置下载按钮
|
||||||
downloadBtn.onclick = function() {
|
downloadBtn.onclick = function() {
|
||||||
const a = document.createElement('a');
|
if (!data.audio_url) {
|
||||||
a.href = data.audio_url;
|
showError('没有找到音频文件');
|
||||||
a.download = 'tts_audio.mp3';
|
return;
|
||||||
document.body.appendChild(a);
|
}
|
||||||
a.click();
|
|
||||||
document.body.removeChild(a);
|
// 创建有意义的文件名 (使用文本前10个字符 + 时间戳)
|
||||||
|
const textPreview = textInput.value.trim().substring(0, 10).replace(/[^a-zA-Z0-9]/g, '_');
|
||||||
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||||||
|
const fileName = `tts_${textPreview}_${timestamp}.mp3`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = data.audio_url;
|
||||||
|
a.download = fileName;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('下载失败:', error);
|
||||||
|
showError('下载失败,请重试');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
showError('语音生成失败: ' + data.message);
|
showError(data.message || '语音生成失败');
|
||||||
emptyResult.style.display = 'block';
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showError('语音生成失败: ' + error.message);
|
console.error('生成语音失败:', error);
|
||||||
emptyResult.style.display = 'block';
|
showError('生成语音失败,请重试');
|
||||||
} finally {
|
} finally {
|
||||||
// 隐藏加载状态
|
// 恢复按钮状态
|
||||||
loading.classList.remove('active');
|
loading.classList.remove('active');
|
||||||
generateBtn.disabled = false;
|
generateBtn.disabled = false;
|
||||||
|
|
||||||
|
// 如果没有音频结果,显示空状态
|
||||||
|
if (audioResult.style.display === 'none') {
|
||||||
|
emptyResult.style.display = 'block';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示错误消息
|
// 绑定生成按钮点击事件
|
||||||
function showError(message) {
|
|
||||||
errorMessage.textContent = message;
|
|
||||||
errorMessage.style.display = 'block';
|
|
||||||
successMessage.style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 显示成功消息
|
|
||||||
function showSuccess(message) {
|
|
||||||
successMessage.textContent = message;
|
|
||||||
successMessage.style.display = 'block';
|
|
||||||
errorMessage.style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 隐藏所有消息
|
|
||||||
function hideMessages() {
|
|
||||||
errorMessage.style.display = 'none';
|
|
||||||
successMessage.style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 事件监听器
|
|
||||||
categorySelect.addEventListener('change', function() {
|
|
||||||
const category = this.value;
|
|
||||||
if (category) {
|
|
||||||
loadVoicesByCategory(category);
|
|
||||||
} else {
|
|
||||||
voiceOptions.innerHTML = '<p>请先选择音色分类</p>';
|
|
||||||
selectedVoiceType = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
generateBtn.addEventListener('click', generateAudio);
|
generateBtn.addEventListener('click', generateAudio);
|
||||||
|
|
||||||
// 初始化
|
// 初始化加载音色分类
|
||||||
loadVoiceCategories();
|
loadVoiceCategories();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
Reference in New Issue
Block a user