99i commited on
Commit
c4d8226
·
verified ·
1 Parent(s): d84a49f

Create templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +243 -0
templates/index.html ADDED
@@ -0,0 +1,243 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>TTS 语音合成服务</title>
8
+ <style>
9
+ body {
10
+ font-family: Arial, sans-serif;
11
+ margin: 0;
12
+ padding: 20px;
13
+ background-color: #f5f5f5;
14
+ }
15
+
16
+ .container {
17
+ max-width: 1200px;
18
+ margin: 0 auto;
19
+ background-color: white;
20
+ padding: 20px;
21
+ border-radius: 8px;
22
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
23
+ }
24
+
25
+ h1 {
26
+ color: #333;
27
+ text-align: center;
28
+ }
29
+
30
+ .filter-section {
31
+ margin-bottom: 20px;
32
+ padding: 15px;
33
+ background-color: #f0f8ff;
34
+ border-radius: 5px;
35
+ }
36
+
37
+ .voice-list {
38
+ display: grid;
39
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
40
+ gap: 20px;
41
+ }
42
+
43
+ .voice-card {
44
+ border: 1px solid #ddd;
45
+ padding: 15px;
46
+ border-radius: 5px;
47
+ background-color: white;
48
+ }
49
+
50
+ .voice-card h3 {
51
+ margin-top: 0;
52
+ color: #2c3e50;
53
+ }
54
+
55
+ .voice-card p {
56
+ margin: 5px 0;
57
+ }
58
+
59
+ .style-list {
60
+ display: flex;
61
+ flex-wrap: wrap;
62
+ gap: 5px;
63
+ margin-top: 10px;
64
+ }
65
+
66
+ .style-tag {
67
+ background-color: #e0f7fa;
68
+ padding: 3px 8px;
69
+ border-radius: 10px;
70
+ font-size: 12px;
71
+ }
72
+
73
+ select,
74
+ button {
75
+ padding: 8px 12px;
76
+ margin-right: 10px;
77
+ border-radius: 4px;
78
+ border: 1px solid #ddd;
79
+ }
80
+
81
+ button {
82
+ background-color: #4CAF50;
83
+ color: white;
84
+ border: none;
85
+ cursor: pointer;
86
+ }
87
+
88
+ button:hover {
89
+ background-color: #45a049;
90
+ }
91
+ </style>
92
+ </head>
93
+
94
+ <body>
95
+ <div class="container">
96
+ <h1>TTS 语音合成服务</h1>
97
+ <div class="container" style="margin-top: 30px;">
98
+ <h2>语音合成</h2>
99
+ <div class="filter-section">
100
+ <div style="margin-bottom: 15px;">
101
+ <label for="synthesis-text">输入文本:</label>
102
+ <textarea id="synthesis-text" rows="4"
103
+ style="width: 100%; padding: 8px; border-radius: 4px; border: 1px solid #ddd;"></textarea>
104
+ </div>
105
+
106
+ <div style="margin-bottom: 15px;">
107
+ <label for="synthesis-voice">选择音色:</label>
108
+ <select id="synthesis-voice" style="width: 200px;">
109
+ <!-- 音色选项将通过JavaScript动态加载 -->
110
+ </select>
111
+ </div>
112
+
113
+ <div style="margin-bottom: 15px;">
114
+ <label for="rate-slider">语速: <span id="rate-value">1.0</span></label>
115
+ <input type="range" id="rate-slider" min="0.5" max="2.0" step="0.1" value="1.0"
116
+ style="width: 200px;" oninput="document.getElementById('rate-value').textContent = this.value">
117
+ </div>
118
+
119
+ <div style="margin-bottom: 15px;">
120
+ <label for="pitch-slider">音调: <span id="pitch-value">0</span></label>
121
+ <input type="range" id="pitch-slider" min="-12" max="12" step="1" value="0" style="width: 200px;"
122
+ oninput="document.getElementById('pitch-value').textContent = this.value">
123
+ </div>
124
+
125
+ <button onclick="synthesizeSpeech()">合成语音</button>
126
+ <audio id="audio-player" controls style="display: none; margin-top: 15px; width: 100%;"></audio>
127
+ </div>
128
+ <div class="filter-section">
129
+ <label for="gender-filter">按性别筛选:</label>
130
+ <select id="gender-filter">
131
+ <option value="all">全部</option>
132
+ <option value="Female">女声</option>
133
+ <option value="Male">男声</option>
134
+ </select>
135
+ <button id="apply-filter">应用筛选</button>
136
+ </div>
137
+
138
+ <div class="voice-list" id="voice-list">
139
+ <!-- 语音列表将通过JavaScript动态加载 -->
140
+ </div>
141
+ </div>
142
+
143
+
144
+ </div>
145
+
146
+ <script>
147
+ async function loadVoices(gender = 'all') {
148
+ try {
149
+ const response = await fetch('/voices?l=zh&d=true');
150
+ let voices = await response.json();
151
+ voices = voices['voices']
152
+ const filteredVoices = gender === 'all'
153
+ ? voices
154
+ : voices.filter(voice => voice.Gender === gender);
155
+
156
+ const voiceList = document.getElementById('voice-list');
157
+ voiceList.innerHTML = '';
158
+
159
+ filteredVoices.forEach(voice => {
160
+ const voiceCard = document.createElement('div');
161
+ voiceCard.className = 'voice-card';
162
+
163
+ voiceCard.innerHTML = `
164
+ <h3>${voice.LocalName} (${voice.DisplayName})</h3>
165
+ <p><strong>性别:</strong> ${voice.Gender === 'Female' ? '女' : '男'}</p>
166
+ <p><strong>语言:</strong> ${voice.LocaleName}</p>
167
+ <p><strong>短名称:</strong> ${voice.ShortName}</p>
168
+
169
+ <div class="style-list">
170
+ ${voice.StyleList ? voice.StyleList.map(style =>
171
+ `<span class="style-tag">${style}</span>`).join('') : ''}
172
+ </div>
173
+ `;
174
+
175
+ voiceList.appendChild(voiceCard);
176
+ });
177
+ } catch (error) {
178
+ console.error('加载音色列表失败:', error);
179
+ document.getElementById('voice-list').innerHTML =
180
+ '<p style="color:red;">加载音色列表失败,请刷新页面重试</p>';
181
+ }
182
+ }
183
+
184
+ async function synthesizeSpeech() {
185
+ const text = document.getElementById('synthesis-text').value;
186
+ const voice = document.getElementById('synthesis-voice').value;
187
+ const rate = document.getElementById('rate-slider').value;
188
+ const pitch = document.getElementById('pitch-slider').value;
189
+
190
+ try {
191
+ const response = await fetch('/tts', {
192
+ method: 'POST',
193
+ headers: {
194
+ 'Content-Type': 'application/json',
195
+ },
196
+ body: JSON.stringify({
197
+ t: text,
198
+ v: voice,
199
+ r: rate,
200
+ p: pitch
201
+ })
202
+ });
203
+
204
+ if (response.ok) {
205
+ let audioUrl = await response.json();
206
+ audioUrl = audioUrl['audio_url']
207
+ const audioPlayer = document.getElementById('audio-player');
208
+ audioPlayer.src = audioUrl;
209
+ audioPlayer.style.display = 'block';
210
+ } else {
211
+ alert('语音合成失败: ' + await response.text());
212
+ }
213
+ } catch (error) {
214
+ console.error('语音合成错误:', error);
215
+ alert('语音合成过程中发生错误');
216
+ }
217
+ }
218
+
219
+ document.addEventListener('DOMContentLoaded', () => {
220
+ loadVoices();
221
+
222
+ // 加载音色选项
223
+ fetch('/voices?l=zh&d=true')
224
+ .then(response => response.json())
225
+ .then(data => {
226
+ const voiceSelect = document.getElementById('synthesis-voice');
227
+ data.voices.forEach(voice => {
228
+ const option = document.createElement('option');
229
+ option.value = voice.ShortName;
230
+ option.textContent = `${voice.LocalName} (${voice.Gender === 'Female' ? '女' : '男'})`;
231
+ voiceSelect.appendChild(option);
232
+ });
233
+ });
234
+
235
+ document.getElementById('apply-filter').addEventListener('click', () => {
236
+ const gender = document.getElementById('gender-filter').value;
237
+ loadVoices(gender);
238
+ });
239
+ });
240
+ </script>
241
+ </body>
242
+
243
+ </html>