222 lines
5.7 KiB
JavaScript
222 lines
5.7 KiB
JavaScript
// DOM元素获取
|
|
const apiKeyInput = document.getElementById('apiKey');
|
|
const apiBaseInput = document.getElementById('apiBase');
|
|
const textInput = document.getElementById('textInput');
|
|
const charCount = document.getElementById('charCount');
|
|
const modelSelect = document.getElementById('model');
|
|
const voiceSelect = document.getElementById('voice');
|
|
const generateBtn = document.getElementById('generateBtn');
|
|
const playBtn = document.getElementById('playBtn');
|
|
const downloadBtn = document.getElementById('downloadBtn');
|
|
const statusDiv = document.getElementById('status');
|
|
const audioPlayer = document.getElementById('audioPlayer');
|
|
|
|
// 当前生成的音频URL
|
|
let currentAudioURL = null;
|
|
|
|
// 初始化函数
|
|
function init() {
|
|
// 加载本地存储的配置
|
|
loadConfig();
|
|
|
|
// 绑定事件监听器
|
|
bindEventListeners();
|
|
|
|
// 初始状态更新
|
|
updateCharCount();
|
|
}
|
|
|
|
// 加载本地存储的配置
|
|
function loadConfig() {
|
|
const savedAPIKey = localStorage.getItem('openai_api_key');
|
|
const savedAPIBase = localStorage.getItem('openai_api_base');
|
|
|
|
if (savedAPIKey) {
|
|
apiKeyInput.value = savedAPIKey;
|
|
}
|
|
|
|
if (savedAPIBase) {
|
|
apiBaseInput.value = savedAPIBase;
|
|
}
|
|
}
|
|
|
|
// 保存配置到本地存储
|
|
function saveConfig() {
|
|
localStorage.setItem('openai_api_key', apiKeyInput.value);
|
|
localStorage.setItem('openai_api_base', apiBaseInput.value);
|
|
}
|
|
|
|
// 绑定事件监听器
|
|
function bindEventListeners() {
|
|
// 文本输入事件 - 更新字符计数
|
|
textInput.addEventListener('input', updateCharCount);
|
|
|
|
// API配置输入事件 - 保存到本地存储
|
|
apiKeyInput.addEventListener('input', saveConfig);
|
|
apiBaseInput.addEventListener('input', saveConfig);
|
|
|
|
// 生成语音按钮点击事件
|
|
generateBtn.addEventListener('click', generateSpeech);
|
|
|
|
// 播放按钮点击事件
|
|
playBtn.addEventListener('click', togglePlay);
|
|
|
|
// 下载按钮点击事件
|
|
downloadBtn.addEventListener('click', downloadAudio);
|
|
|
|
// 音频播放结束事件
|
|
audioPlayer.addEventListener('ended', onAudioEnded);
|
|
}
|
|
|
|
// 更新字符计数
|
|
function updateCharCount() {
|
|
const count = textInput.value.length;
|
|
charCount.textContent = count;
|
|
}
|
|
|
|
// 生成语音
|
|
async function generateSpeech() {
|
|
// 验证输入
|
|
if (!validateInput()) {
|
|
return;
|
|
}
|
|
|
|
// 保存配置
|
|
saveConfig();
|
|
|
|
// 更新状态
|
|
setStatus('loading', '正在生成语音...');
|
|
disableControls();
|
|
|
|
try {
|
|
// 构建请求数据
|
|
const requestData = {
|
|
api_key: apiKeyInput.value,
|
|
text: textInput.value,
|
|
voice: voiceSelect.value,
|
|
model: modelSelect.value,
|
|
api_base: apiBaseInput.value
|
|
};
|
|
|
|
// 发送请求到后端API
|
|
const response = await fetch('/api/tts', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(requestData)
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json().catch(() => ({
|
|
error: `HTTP错误: ${response.status} ${response.statusText}`
|
|
}));
|
|
throw new Error(errorData.error || '生成语音失败');
|
|
}
|
|
|
|
// 获取音频Blob
|
|
const audioBlob = await response.blob();
|
|
|
|
// 创建音频URL
|
|
if (currentAudioURL) {
|
|
URL.revokeObjectURL(currentAudioURL);
|
|
}
|
|
currentAudioURL = URL.createObjectURL(audioBlob);
|
|
|
|
// 更新音频播放器
|
|
audioPlayer.src = currentAudioURL;
|
|
|
|
// 更新状态
|
|
setStatus('success', '语音生成成功!');
|
|
enableControls();
|
|
|
|
} catch (error) {
|
|
// 处理错误
|
|
setStatus('error', `生成失败: ${error.message}`);
|
|
disableControls();
|
|
}
|
|
}
|
|
|
|
// 验证输入
|
|
function validateInput() {
|
|
if (!apiKeyInput.value.trim()) {
|
|
setStatus('error', '请输入API Key');
|
|
apiKeyInput.focus();
|
|
return false;
|
|
}
|
|
|
|
if (!textInput.value.trim()) {
|
|
setStatus('error', '请输入要转换的文本');
|
|
textInput.focus();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// 切换播放状态
|
|
function togglePlay() {
|
|
if (audioPlayer.paused) {
|
|
audioPlayer.play();
|
|
playBtn.textContent = '暂停';
|
|
} else {
|
|
audioPlayer.pause();
|
|
playBtn.textContent = '播放';
|
|
}
|
|
}
|
|
|
|
// 音频播放结束
|
|
function onAudioEnded() {
|
|
playBtn.textContent = '播放';
|
|
}
|
|
|
|
// 下载音频
|
|
function downloadAudio() {
|
|
if (!currentAudioURL) {
|
|
return;
|
|
}
|
|
|
|
// 创建下载链接
|
|
const link = document.createElement('a');
|
|
link.href = currentAudioURL;
|
|
link.download = 'speech.mp3';
|
|
|
|
// 触发下载
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
}
|
|
|
|
// 设置状态
|
|
function setStatus(type, message) {
|
|
statusDiv.className = `status ${type}`;
|
|
statusDiv.textContent = message;
|
|
|
|
// 清除成功状态
|
|
if (type === 'success') {
|
|
setTimeout(() => {
|
|
statusDiv.className = 'status';
|
|
statusDiv.textContent = '';
|
|
}, 3000);
|
|
}
|
|
}
|
|
|
|
// 禁用控件
|
|
function disableControls() {
|
|
generateBtn.disabled = true;
|
|
playBtn.disabled = true;
|
|
downloadBtn.disabled = true;
|
|
generateBtn.classList.add('loading');
|
|
}
|
|
|
|
// 启用控件
|
|
function enableControls() {
|
|
generateBtn.disabled = false;
|
|
playBtn.disabled = false;
|
|
downloadBtn.disabled = false;
|
|
generateBtn.classList.remove('loading');
|
|
}
|
|
|
|
// 页面加载完成后初始化
|
|
document.addEventListener('DOMContentLoaded', init);
|