Skip to content

Commit 4a6b45e

Browse files
author
developer
committed
回滚
1 parent b6b4c17 commit 4a6b45e

File tree

2 files changed

+175
-184
lines changed

2 files changed

+175
-184
lines changed

index.html

-9
Original file line numberDiff line numberDiff line change
@@ -81,15 +81,6 @@ <h2>历史记录</h2>
8181
<p><a href="https://zwei.de.eu.org" target="_blank">Zwei</a> | <a href="https://github.com/bestZwei/text2voice-web" target="_blank">Github</a></p>
8282
</footer>
8383

84-
<!-- 环境变量替换脚本 -->
85-
<script>
86-
(function() {
87-
window.ENV = {
88-
API_KEY: "{{ API_KEY }}"
89-
};
90-
})();
91-
</script>
92-
9384
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
9485
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.1/umd/popper.min.js"></script>
9586
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>

script.js

+175-175
Original file line numberDiff line numberDiff line change
@@ -1,177 +1,177 @@
1-
const apiConfig = {
2-
"voice-api": {
3-
url: "https://ttsapi.zwei.de.eu.org/tts",
4-
speakers: {
5-
"zh-CN-XiaoxiaoNeural": "晓晓",
6-
"zh-CN-YunxiNeural": "云希",
7-
"zh-CN-YunjianNeural": "云健",
8-
"zh-CN-XiaoyiNeural": "晓伊",
9-
"zh-CN-YunyangNeural": "云扬",
10-
"zh-CN-XiaochenNeural": "晓辰",
11-
"zh-CN-XiaochenMultilingualNeural": "晓辰 多语言",
12-
"zh-CN-XiaohanNeural": "晓涵",
13-
"zh-CN-XiaomengNeural": "晓梦",
14-
"zh-CN-XiaomoNeural": "晓墨",
15-
"zh-CN-XiaoqiuNeural": "晓秋",
16-
"zh-CN-XiaorouNeural": "晓柔",
17-
"zh-CN-XiaoruiNeural": "晓睿",
18-
"zh-CN-XiaoshuangNeural": "晓双",
19-
"zh-CN-XiaoxiaoDialectsNeural": "晓晓 方言",
20-
"zh-CN-XiaoxiaoMultilingualNeural": "晓晓 多语言",
21-
"zh-CN-XiaoyanNeural": "晓颜",
22-
"zh-CN-XiaoyouNeural": "晓悠",
23-
"zh-CN-XiaoyuMultilingualNeural": "晓宇 多语言",
24-
"zh-CN-XiaozhenNeural": "晓甄",
25-
"zh-CN-YunfengNeural": "云枫",
26-
"zh-CN-YunhaoNeural": "云皓",
27-
"zh-CN-YunjieNeural": "云杰",
28-
"zh-CN-YunxiaNeural": "云夏",
29-
"zh-CN-YunyeNeural": "云野",
30-
"zh-CN-YunyiMultilingualNeural": "云逸 多语言",
31-
"zh-CN-YunzeNeural": "云泽",
32-
"zh-CN-YunfanMultilingualNeural": "云帆 多语言",
33-
"zh-CN-YunxiaoMultilingualNeural": "云萧 多语言",
34-
"zh-CN-guangxi-YunqiNeural": "云奇 广西",
35-
"zh-CN-henan-YundengNeural": "云登",
36-
"zh-CN-liaoning-XiaobeiNeural": "晓北 辽宁",
37-
"zh-CN-liaoning-YunbiaoNeural": "云彪 辽宁",
38-
"zh-CN-shaanxi-XiaoniNeural": "晓妮",
39-
"zh-CN-shandong-YunxiangNeural": "云翔",
40-
"zh-CN-sichuan-YunxiNeural": "云希 四川",
41-
"zh-HK-HiuMaanNeural": "曉曼",
42-
"zh-HK-WanLungNeural": "雲龍",
43-
"zh-HK-HiuGaaiNeural": "曉佳",
44-
"zh-TW-HsiaoChenNeural": "曉臻",
45-
"zh-TW-YunJheNeural": "雲哲",
46-
"zh-TW-HsiaoYuNeural": "曉雨"
47-
}
48-
}
49-
};
50-
51-
function updateSpeakerOptions(apiName) {
52-
const speakers = apiConfig[apiName].speakers;
53-
const speakerSelect = $('#speaker');
54-
speakerSelect.empty();
55-
Object.entries(speakers).forEach(([key, value]) => {
56-
speakerSelect.append(new Option(value, key));
57-
});
1+
const apiConfig = {
2+
"voice-api": {
3+
url: "https://ttsapi.zwei.de.eu.org/tts",
4+
speakers: {
5+
"zh-CN-XiaoxiaoNeural": "晓晓",
6+
"zh-CN-YunxiNeural": "云希",
7+
"zh-CN-YunjianNeural": "云健",
8+
"zh-CN-XiaoyiNeural": "晓伊",
9+
"zh-CN-YunyangNeural": "云扬",
10+
"zh-CN-XiaochenNeural": "晓辰",
11+
"zh-CN-XiaochenMultilingualNeural": "晓辰 多语言",
12+
"zh-CN-XiaohanNeural": "晓涵",
13+
"zh-CN-XiaomengNeural": "晓梦",
14+
"zh-CN-XiaomoNeural": "晓墨",
15+
"zh-CN-XiaoqiuNeural": "晓秋",
16+
"zh-CN-XiaorouNeural": "晓柔",
17+
"zh-CN-XiaoruiNeural": "晓睿",
18+
"zh-CN-XiaoshuangNeural": "晓双",
19+
"zh-CN-XiaoxiaoDialectsNeural": "晓晓 方言",
20+
"zh-CN-XiaoxiaoMultilingualNeural": "晓晓 多语言",
21+
"zh-CN-XiaoyanNeural": "晓颜",
22+
"zh-CN-XiaoyouNeural": "晓悠",
23+
"zh-CN-XiaoyuMultilingualNeural": "晓宇 多语言",
24+
"zh-CN-XiaozhenNeural": "晓甄",
25+
"zh-CN-YunfengNeural": "云枫",
26+
"zh-CN-YunhaoNeural": "云皓",
27+
"zh-CN-YunjieNeural": "云杰",
28+
"zh-CN-YunxiaNeural": "云夏",
29+
"zh-CN-YunyeNeural": "云野",
30+
"zh-CN-YunyiMultilingualNeural": "云逸 多语言",
31+
"zh-CN-YunzeNeural": "云泽",
32+
"zh-CN-YunfanMultilingualNeural": "云帆 多语言",
33+
"zh-CN-YunxiaoMultilingualNeural": "云萧 多语言",
34+
"zh-CN-guangxi-YunqiNeural": "云奇 广西",
35+
"zh-CN-henan-YundengNeural": "云登",
36+
"zh-CN-liaoning-XiaobeiNeural": "晓北 辽宁",
37+
"zh-CN-liaoning-YunbiaoNeural": "云彪 辽宁",
38+
"zh-CN-shaanxi-XiaoniNeural": "晓妮",
39+
"zh-CN-shandong-YunxiangNeural": "云翔",
40+
"zh-CN-sichuan-YunxiNeural": "云希 四川",
41+
"zh-HK-HiuMaanNeural": "曉曼",
42+
"zh-HK-WanLungNeural": "雲龍",
43+
"zh-HK-HiuGaaiNeural": "曉佳",
44+
"zh-TW-HsiaoChenNeural": "曉臻",
45+
"zh-TW-YunJheNeural": "雲哲",
46+
"zh-TW-HsiaoYuNeural": "曉雨"
47+
}
48+
}
49+
};
50+
51+
function updateSpeakerOptions(apiName) {
52+
const speakers = apiConfig[apiName].speakers;
53+
const speakerSelect = $('#speaker');
54+
speakerSelect.empty();
55+
Object.entries(speakers).forEach(([key, value]) => {
56+
speakerSelect.append(new Option(value, key));
57+
});
58+
}
59+
60+
function updateSliderLabel(sliderId, labelId) {
61+
const slider = $(`#${sliderId}`);
62+
const label = $(`#${labelId}`);
63+
label.text(slider.val());
64+
slider.on('input', function () {
65+
label.text(this.value);
66+
});
67+
}
68+
69+
$(document).ready(function () {
70+
// 启用工具提示
71+
$('[data-toggle="tooltip"]').tooltip();
72+
73+
// 设置初始API为voice-api
74+
updateSpeakerOptions('voice-api');
75+
76+
// 更新所选 API 的讲述人选项
77+
$('#api').on('change', function () {
78+
updateSpeakerOptions(this.value);
79+
});
80+
81+
// 初始化语速和语调滑块
82+
updateSliderLabel('rate', 'rateValue');
83+
updateSliderLabel('pitch', 'pitchValue');
84+
85+
$('#text2voice-form').on('submit', function (event) {
86+
event.preventDefault();
87+
generateVoice(false);
88+
});
89+
90+
$('#previewButton').on('click', function () {
91+
generateVoice(true);
92+
});
93+
});
94+
95+
function generateVoice(isPreview) {
96+
const apiName = $('#api').val();
97+
const apiUrl = apiConfig[apiName].url;
98+
const speaker = $('#speaker').val();
99+
const text = $('#text').val();
100+
const previewText = isPreview ? text.substring(0, 20) : text; // 预览时获取前20个字
101+
let url = `${apiUrl}?t=${encodeURIComponent(previewText)}&v=${encodeURIComponent(speaker)}`;
102+
103+
const rate = $('#rate').val();
104+
const pitch = $('#pitch').val();
105+
url += `&r=${encodeURIComponent(rate)}&p=${encodeURIComponent(pitch)}&o=audio-24khz-48kbitrate-mono-mp3`;
106+
107+
$('#loading').show();
108+
$('#result').hide();
109+
$('#generateButton').prop('disabled', true);
110+
$('#previewButton').prop('disabled', true);
111+
112+
$.ajax({
113+
url: url,
114+
method: 'GET',
115+
headers: {
116+
'x-api-key': '@ak47' // 添加 API 密钥
117+
},
118+
xhrFields: {
119+
responseType: 'blob' // 确保返回的是一个Blob对象
120+
},
121+
success: function (blob) {
122+
const voiceUrl = URL.createObjectURL(blob);
123+
$('#audio').attr('src', voiceUrl);
124+
$('#audio')[0].load(); // 确保加载音频文件
125+
if (!isPreview) {
126+
$('#download').attr('href', voiceUrl);
127+
const timestamp = new Date().toLocaleTimeString(); // 获取当前时间
128+
const shortenedText = text.length > 5 ? text.substring(0, 5) + '...' : text; // 截取前5个字
129+
addHistoryItem(timestamp, shortenedText, voiceUrl);
58130
}
59-
60-
function updateSliderLabel(sliderId, labelId) {
61-
const slider = $(`#${sliderId}`);
62-
const label = $(`#${labelId}`);
63-
label.text(slider.val());
64-
slider.on('input', function () {
65-
label.text(this.value);
66-
});
131+
$('#result').show();
132+
$('#loading').hide();
133+
$('#generateButton').prop('disabled', false);
134+
$('#previewButton').prop('disabled', false);
135+
},
136+
error: function () {
137+
alert('请求失败,请检查网络连接');
138+
$('#loading').hide();
139+
$('#generateButton').prop('disabled', false);
140+
$('#previewButton').prop('disabled', false);
67141
}
68-
69-
$(document).ready(function () {
70-
// 启用工具提示
71-
$('[data-toggle="tooltip"]').tooltip();
72-
73-
// 设置初始API为voice-api
74-
updateSpeakerOptions('voice-api');
75-
76-
// 更新所选 API 的讲述人选项
77-
$('#api').on('change', function () {
78-
updateSpeakerOptions(this.value);
79-
});
80-
81-
// 初始化语速和语调滑块
82-
updateSliderLabel('rate', 'rateValue');
83-
updateSliderLabel('pitch', 'pitchValue');
84-
85-
$('#text2voice-form').on('submit', function (event) {
86-
event.preventDefault();
87-
generateVoice(false);
88-
});
89-
90-
$('#previewButton').on('click', function () {
91-
generateVoice(true);
92-
});
93-
});
94-
95-
function generateVoice(isPreview) {
96-
const apiName = $('#api').val();
97-
const apiUrl = apiConfig[apiName].url;
98-
const speaker = $('#speaker').val();
99-
const text = $('#text').val();
100-
const previewText = isPreview ? text.substring(0, 20) : text; // 预览时获取前20个字
101-
let url = `${apiUrl}?t=${encodeURIComponent(previewText)}&v=${encodeURIComponent(speaker)}`;
102-
103-
const rate = $('#rate').val();
104-
const pitch = $('#pitch').val();
105-
url += `&r=${encodeURIComponent(rate)}&p=${encodeURIComponent(pitch)}&o=audio-24khz-48kbitrate-mono-mp3`;
106-
107-
$('#loading').show();
108-
$('#result').hide();
109-
$('#generateButton').prop('disabled', true);
110-
$('#previewButton').prop('disabled', true);
111-
112-
$.ajax({
113-
url: url,
114-
method: 'GET',
115-
headers: {
116-
'x-api-key': window.ENV.API_KEY // 动态插入 API 密钥
117-
},
118-
xhrFields: {
119-
responseType: 'blob' // 确保返回的是一个Blob对象
120-
},
121-
success: function (blob) {
122-
const voiceUrl = URL.createObjectURL(blob);
123-
$('#audio').attr('src', voiceUrl);
124-
$('#audio')[0].load(); // 确保加载音频文件
125-
if (!isPreview) {
126-
$('#download').attr('href', voiceUrl);
127-
const timestamp = new Date().toLocaleTimeString(); // 获取当前时间
128-
const shortenedText = text.length > 5 ? text.substring(0, 5) + '...' : text; // 截取前5个字
129-
addHistoryItem(timestamp, shortenedText, voiceUrl);
130-
}
131-
$('#result').show();
132-
$('#loading').hide();
133-
$('#generateButton').prop('disabled', false);
134-
$('#previewButton').prop('disabled', false);
135-
},
136-
error: function () {
137-
alert('请求失败,请检查网络连接');
138-
$('#loading').hide();
139-
$('#generateButton').prop('disabled', false);
140-
$('#previewButton').prop('disabled', false);
141-
}
142-
});
143-
}
144-
145-
function addHistoryItem(timestamp, text, audioURL) {
146-
const historyItems = $('#historyItems');
147-
const historyItem = $(`
148-
<div class="history-item">
149-
<span>${timestamp} - ${text}</span>
150-
<div>
151-
<button class="btn btn-secondary" onclick="playAudio('${audioURL}')">播放</button>
152-
<button class="btn btn-info" onclick="downloadAudio('${audioURL}')">下载</button>
153-
</div>
154-
</div>
155-
`);
156-
historyItems.append(historyItem);
157-
}
158-
159-
function playAudio(audioURL) {
160-
const audioElement = $('#audio')[0];
161-
audioElement.src = audioURL;
162-
audioElement.load(); // 确保加载音频文件
163-
audioElement.play();
164-
}
165-
166-
function downloadAudio(audioURL) {
167-
const link = document.createElement('a');
168-
link.href = audioURL;
169-
link.download = 'audio.mp3';
170-
link.click();
171-
}
172-
173-
function clearHistory() {
174-
$('#historyItems').empty();
175-
alert("历史记录已清除!");
176-
}
177-
142+
});
143+
}
144+
145+
function addHistoryItem(timestamp, text, audioURL) {
146+
const historyItems = $('#historyItems');
147+
const historyItem = $(`
148+
<div class="history-item">
149+
<span>${timestamp} - ${text}</span>
150+
<div>
151+
<button class="btn btn-secondary" onclick="playAudio('${audioURL}')">播放</button>
152+
<button class="btn btn-info" onclick="downloadAudio('${audioURL}')">下载</button>
153+
</div>
154+
</div>
155+
`);
156+
historyItems.append(historyItem);
157+
}
158+
159+
function playAudio(audioURL) {
160+
const audioElement = $('#audio')[0];
161+
audioElement.src = audioURL;
162+
audioElement.load(); // 确保加载音频文件
163+
audioElement.play();
164+
}
165+
166+
function downloadAudio(audioURL) {
167+
const link = document.createElement('a');
168+
link.href = audioURL;
169+
link.download = 'audio.mp3';
170+
link.click();
171+
}
172+
173+
function clearHistory() {
174+
$('#historyItems').empty();
175+
alert("历史记录已清除!");
176+
}
177+

0 commit comments

Comments
 (0)