Xinonria commited on
Commit
220f037
·
verified ·
1 Parent(s): 76d8c82

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +391 -392
app.py CHANGED
@@ -1,392 +1,391 @@
1
- from io import StringIO
2
- import time
3
- import os
4
- import logging
5
-
6
- import gradio as gr
7
- import pandas as pd
8
- from pypinyin import lazy_pinyin
9
- from gradio_i18n import , Translate
10
-
11
- from api import generate_api
12
-
13
- # 翻译文件位置
14
- trans_file = os.path.join(os.path.dirname(__file__),"i18n", "translations.json")
15
-
16
- # 关闭aiohttp的DEBUG日志
17
- logging.getLogger('aiohttp').setLevel(logging.WARNING)
18
- logging.getLogger("gradio").setLevel(logging.WARNING)
19
-
20
- # 带有时间的log
21
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
22
-
23
-
24
- terms = r"""
25
- ## 免责声明
26
-
27
- 本网站提供的语音合成服务(以下简称“服务”)旨在供个人使用和娱乐目的。在使用本服务前,请用户仔细阅读并充分理解以下条款:
28
-
29
- 1. **角色版权**:本网站可能使用的角色形象涉及第三方知识产权。本网站不拥有这些角色的版权。用户在使用服务时应尊重相关角色的知识产权,并确保其行为不侵犯任何第三方的知识产权。
30
-
31
- 2. **用户生成内容(UGC)**:用户通过本平台生成的语音内容(以下简称“UGC”)由用户自行负责,与本平台无关。本平台无法控制或审核用户生成的具体内容,且不对UGC的准确性、完整性或合法性承担任何责任。
32
-
33
- 3. **使用限制**:本服务生成的语音及其UGC仅限于个人使用,不得用于任何商业目的。未经本平台事先书面同意,禁止将生成内容用于任何商业活动。
34
-
35
- 4. **法律责任**:用户使用本服务所产生的任何法律责任由用户自行承担,与本平台无关。如因用户使用服务或其UGC导致的任何纠纷或损失,本平台不承担任何责任。
36
-
37
- 5. **版权声明**:用户应尊重原创,不得使用本服务生成侵犯他人著作权的内容。如发现用户生成内容侵犯他人版权,本平台有权立即停止对其提供服务,并保留追究法律责任的权利。
38
-
39
- 6. **内容监管**:尽管本平台无法控制UGC,但一旦发现违反本免责声明或法律法规的内容,本平台将采取必要措施,包括但不限于删除违规内容,并配合有关部门进行调查。
40
-
41
- 7. **注明要求**:用户应在生成内容的显著位置,如可能的话,注明“此内容由RubiiTTS生成”或类似的说明。用户应确保注明行为符合本条款的要求。
42
-
43
- 用户使用本网站即表示同意以上免责声明。如有疑问,请联系我们contact@yomio.ai。
44
-
45
- **最终解释权归本网站所有。**
46
-
47
- """
48
-
49
- terms_js = r"alert('本网站提供的语音合成服务仅供个人使用和娱乐目的。请注意以下几点:\n1. 角色版权:本网站使用的角色形象可能涉及第三方知识产权,我们不拥有这些角色的版权。\n2. 生成内容:用户通过本平台生成的语音内容由用户自行负责,与本平台无关。我们无法控制或审核用户生成的内容。\n3. 使用限制:生成的语音仅限个人使用,不得用于任何商业目的。\n4. 法律责任:用户使用本服务所产生的任何法律责任由用户自行承担,与本平台无关。\n5. 版权声明:请尊重原创,不要使用本服务生成侵犯他人著作权的内容。\n使用本网站即表示您同意以上免责声明。如有疑问,请联系我们。')"
50
-
51
- def load_characters_csv(lang):
52
- name = f"characters_{lang}"
53
- return pd.read_csv(StringIO(os.getenv(name)))
54
-
55
- def update_all_characters(lang, current_all_characters):
56
- new_characters = load_characters_csv(lang)
57
- initial_characters = get_characters(kind="原神", all_characters=new_characters)
58
- return new_characters, initial_characters, gr.Gallery(value=[[char['头像'], char['名称']] for char in initial_characters],
59
- show_label=False, elem_id="character_gallery", columns=[11],
60
- object_fit="contain", height="auto", interactive=False,
61
- allow_preview=False, selected_index=None)
62
-
63
- def get_characters(query=None, page=1, per_page=400, kind="原神", lang="zh", all_characters=None):
64
- # 使用传入的 all_characters 参数
65
- filtered_characters = all_characters[all_characters["类别"] == kind]
66
-
67
- if query:
68
- # 使用拼音和汉字进行搜索
69
- filtered_characters = filtered_characters[
70
- filtered_characters['名称'].str.contains(query, case=False)
71
- ]
72
- if filtered_characters.empty and lang == 'zh':
73
- filtered_characters = all_characters[all_characters["类别"] == kind]
74
- filtered_characters = filtered_characters[
75
- filtered_characters['名称'].apply(lambda x: ''.join(lazy_pinyin(x))).str.contains(query, case=False)
76
- ]
77
-
78
- # 按名称分组,并选择每组的第一个记录
79
- unique_characters = filtered_characters.groupby('名称').first().reset_index().sort_values(by='id')
80
-
81
- # 应用分页
82
- start_index = (page - 1) * per_page
83
- end_index = start_index + per_page
84
-
85
- return unique_characters.iloc[start_index:end_index].to_dict('records')
86
-
87
- async def generate(selected_character = None, selected_characters = [], text = "", lang="zh"):
88
- # print("-------",selected_character)
89
- # print("-------",selected_characters)
90
- if selected_character:
91
- characters = [selected_character] + selected_characters
92
- else:
93
- characters = selected_characters
94
- if not selected_character and not selected_characters:
95
- if lang == "zh":
96
- raise gr.Error("请先选择一个角色")
97
- elif lang == "en":
98
- raise gr.Error("Please select a character first")
99
- elif lang == "ja":
100
- raise gr.Error("まず、キャラクターを選択してください")
101
- elif lang == "ko":
102
- raise gr.Error("먼저 캐릭터를 선택하세요")
103
- voice_ids = [char.get("voice_id") for char in characters if char.get("voice_id")]
104
-
105
- if not voice_ids:
106
- raise gr.Error("所选角色没有关联的 voice_id")
107
-
108
- start_time = time.time()
109
- # 假设我们只使用第一个选择的角色的名称
110
- if voice_ids == "1":
111
- if lang == "zh":
112
- raise gr.Error("该角色暂未创建语音")
113
- elif lang == "en":
114
- raise gr.Error("The character has not been created yet")
115
- elif lang == "ja":
116
- raise gr.Error("そのキャラクターの音声はまだ作成されていません")
117
- elif lang == "ko":
118
- raise gr.Error("해당 캐릭터의 음성이 아직 생성되지 않았습니다")
119
-
120
- if text == "":
121
- if lang == "zh":
122
- raise gr.Error("请输入需要合成的文本")
123
- elif lang == "en":
124
- raise gr.Error("Please enter the text to be synthesized")
125
- elif lang == "ja":
126
- raise gr.Error("合成するテキストを入力してください")
127
- elif lang == "ko":
128
- raise gr.Error("합성할 텍스트를 입력하세요")
129
-
130
- if (lang == "en" and len(text.split()) > 200) or len(text) > 512:
131
- if lang == "zh":
132
- raise gr.Error("长度请控制在512个字符以内")
133
- elif lang == "en":
134
- raise gr.Error("The text length exceeds 200 words")
135
- elif lang == "ja":
136
- raise gr.Error("テキストの長さが512文字を超えています")
137
- elif lang == "ko":
138
- raise gr.Error("텍스트 길이가 512자를 초과합니다")
139
-
140
- # logging.info(f"选择角色: {characters[0].get('名称')}, 文本: {text}, voice_id: {voice_ids}")
141
- audio = await generate_api(voice_ids, text)
142
- end_time = time.time()
143
- if lang == "zh":
144
- cost_time = f"合成共花费{end_time - start_time:.2f}秒"
145
- elif lang == "en":
146
- cost_time = f"Total time spent synthesizing: {end_time - start_time:.2f} seconds"
147
- elif lang == "ja":
148
- cost_time = f"合成にかかった時間: {end_time - start_time:.2f}秒"
149
- elif lang == "ko":
150
- cost_time = f"합성에 소요된 시간: {end_time - start_time:.2f}초"
151
- if isinstance(audio, str):
152
- print(audio)
153
- raise gr.Error(audio)
154
- else:
155
- return audio, cost_time
156
-
157
- def get_character_emotions(character, all_characters):
158
- # all_characters中筛选出与当前角色名称相同的所有记录
159
- character_records = all_characters[all_characters['名称'] == character['名称']]
160
-
161
- # 获取所有不重复的情绪
162
- emotions = character_records['情绪'].unique().tolist()
163
-
164
- # 如果没有找到情绪,返回一个默认值
165
- return emotions if emotions else ["默认情绪"]
166
-
167
- def update_character_info(character_name, emotion, current_character, all_characters):
168
- character_info = None
169
- if character_name and emotion:
170
- character_info = all_characters[(all_characters['名称'] == character_name) & (all_characters['情绪'] == emotion)]
171
- if character_name == "":
172
- return None
173
- character_info = character_info.iloc[0].to_dict()
174
- return character_info, all_characters
175
-
176
- def add_new_voice(current_character, selected_characters, kind, lang, all_characters):
177
- if not current_character:
178
- if lang == "zh":
179
- raise gr.Error("请先选择一个角色")
180
- elif lang == "en":
181
- raise gr.Error("Please select a character first")
182
- elif lang == "ja":
183
- raise gr.Error("まず、キャラクターを選択してください")
184
- elif lang == "ko":
185
- raise gr.Error("먼저 캐릭터를 선택하세요")
186
-
187
- if len(selected_characters) >= 5:
188
- raise gr.Error("已达到最大选择数(5个)")
189
-
190
- # 检查是否已存在相同角色
191
- existing_char = next((char for char in selected_characters if char['名称'] == current_character['名称']), None)
192
- if existing_char:
193
- # 如果情绪不同,更新情绪
194
- if existing_char['情绪'] != current_character['情绪']:
195
- existing_char['情绪'] = current_character['情绪']
196
- else:
197
- selected_characters.insert(0, current_character)
198
-
199
- updated_characters = get_characters(kind=kind, lang=lang, all_characters=all_characters)
200
- # ! 取消gallery选中状态,返回个新的gallery是必要的,否则会保留上一次的选中状态。这里sonnet很喜欢改成返回一个数组,但这不能清空gallery的选中状态
201
- updated_gallery = gr.Gallery(value=[[char['头像'], char['名称']] for char in updated_characters],
202
- show_label=False, elem_id="character_gallery", columns=[11],
203
- object_fit="contain", height="auto", interactive=False,
204
- allow_preview=False, selected_index=None)
205
-
206
- return (None, gr.update(value=""), gr.update(choices=[]), selected_characters,
207
- updated_characters, updated_gallery, gr.update(visible=True), all_characters)
208
-
209
- def update_selected_chars_display(selected_characters):
210
- updates = []
211
- for i, (name, emotion, _, row) in enumerate(selected_chars_rows):
212
- if i < len(selected_characters):
213
- char = selected_characters[i]
214
- updates.extend([
215
- gr.update(value=char['名称'], visible=True),
216
- gr.update(value=char['情绪'], visible=True),
217
- gr.update(visible=True),
218
- gr.update(visible=True)
219
- ])
220
- else:
221
- updates.extend([
222
- gr.update(value="", visible=False),
223
- gr.update(value="", visible=False),
224
- gr.update(visible=False),
225
- gr.update(visible=False)
226
- ])
227
- return updates
228
-
229
- def remove_character(index, selected_characters):
230
- if 0 <= index < len(selected_characters):
231
- del selected_characters[index]
232
- return selected_characters, gr.update(visible=True)
233
-
234
- def update_gallery(kind, query, all_characters):
235
- updated_characters = get_characters(kind=kind, query=query, lang=lang, all_characters=all_characters)
236
- return updated_characters, [[char['头像'], char['名称']] for char in updated_characters], all_characters
237
-
238
- def on_select(evt: gr.SelectData, characters, selected_characters, all_characters):
239
- # 如果没有选择角色,换人的时候清空
240
- if len(selected_characters) == 0:
241
- selected_characters = []
242
-
243
- selected = characters[evt.index]
244
- emotions = get_character_emotions(selected, all_characters)
245
- default_emotion = emotions[0] if emotions else ""
246
-
247
- character_dict = selected.copy()
248
- character_dict['情绪'] = default_emotion
249
-
250
- return selected["名称"], gr.Dropdown(choices=emotions, value=default_emotion), character_dict, selected_characters
251
-
252
- with gr.Blocks(title="Rubii TTS", theme=gr.themes.Soft()) as demo:
253
- lang = gr.Radio(choices=[("中文", "zh"), ("English", "en"), ("日本語", "ja"), ("한국인", "ko")], label=("Language"), value="zh", scale=1)
254
- all_characters_state = gr.State(load_characters_csv("zh"))
255
-
256
- # with Translate(trans_file, lang, placeholder_langs=["en", "zh", "ja", "ko"]):
257
- gr.Markdown(
258
- value=("""## 🎉 欢迎使用Rubii语音合成系统 🎉
259
-
260
- #### [🗣️ 不想只是听到角色的声音,还想与他们进行互动交流吗?快点击我来体验与这些角色的生动对话吧!(中国大陆暂不可用) 🌟](https://rubii.ai)
261
-
262
- 📝 使用说明:
263
- 1. 选择角色类别 🎭
264
- 2. 从图库中选择一个或多个角色(最多5个) 👥。当选择多个角色时,系统会自动进行声线融合(以第一个角色为主音色,其他角色为辅助音色),您可以尝试不同的组合来获得独特的声音效果。
265
- 3. 选择角色的情绪 😊😢😠
266
- 4. 输入要合成的文本 ✍️
267
- 5. 点击"合成语音"按钮 🔊
268
- """
269
- ))
270
- with gr.Group():
271
- initial_characters = get_characters(kind="原神", lang="zh", all_characters=all_characters_state.value)
272
- characters = gr.State(initial_characters)
273
- selected_characters = gr.State([])
274
- current_character = gr.State(None)
275
-
276
- with gr.Blocks():
277
- with gr.Row():
278
- # kind = gr.Dropdown(choices=["原神", "崩坏星穹铁道","鸣潮","明日方舟","其他"], value="原神", label="请选择角色类别")
279
- choices = ["原神", "崩坏星穹铁道", "鸣潮"]
280
- kind = gr.Dropdown(choices=[((name), name) for name in choices], value="原神", label=("选择角色类别"))
281
- query = gr.Textbox(label=("搜索角色"), value="", lines=1, max_lines=1, interactive=True)
282
- with gr.Blocks():
283
- gallery = gr.Gallery(
284
- value=[[char['头像'], char['名称']] for char in characters.value],
285
- show_label=False,
286
- elem_id="character_gallery",
287
- columns=[11],
288
- object_fit="contain",
289
- height="auto",
290
- interactive=False,
291
- allow_preview=False,
292
- selected_index=None
293
- )
294
- with gr.Row():
295
- character_name = gr.Textbox(label=("当前选择的角色"), interactive=False, max_lines=1)
296
- info_type = gr.Dropdown(choices=[], label=("选择情绪"))
297
- with gr.Row():
298
- add_voice_button = gr.Button(("添加新的声音"), variant="primary")
299
-
300
- selected_chars_container = gr.Column(elem_id="selected_chars_container", visible=False)
301
-
302
- with selected_chars_container:
303
- gr.Markdown(("### 已选择的角色"))
304
- selected_chars_rows = []
305
- for i in range(5): # 假设最多选择5个角色
306
- with gr.Row() as row:
307
- name = gr.Textbox(label=("名称"), interactive=False, max_lines=1)
308
- emotion = gr.Textbox(label=("情绪"), interactive=False, max_lines=1)
309
- delete_btn = gr.Button(("删除"), scale=0)
310
- selected_chars_rows.append((name, emotion, delete_btn, row))
311
-
312
-
313
- # -------------- 绑定事件 --------------
314
-
315
- lang.change(
316
- fn=update_all_characters,
317
- inputs=[lang, all_characters_state],
318
- outputs=[all_characters_state, characters, gallery]
319
- )
320
-
321
- add_voice_button.click(
322
- fn=add_new_voice,
323
- inputs=[current_character, selected_characters, kind, lang, all_characters_state],
324
- outputs=[current_character, character_name, info_type, selected_characters,
325
- characters, gallery, selected_chars_container, all_characters_state]
326
- ).then(
327
- fn=update_selected_chars_display,
328
- inputs=[selected_characters],
329
- outputs=[item for row in selected_chars_rows for item in row]
330
- )
331
-
332
-
333
- gallery.select(
334
- fn=on_select,
335
- inputs=[characters, selected_characters, all_characters_state],
336
- outputs=[character_name, info_type, current_character, selected_characters]
337
- )
338
-
339
- info_type.change(
340
- fn=update_character_info,
341
- inputs=[character_name, info_type, current_character, all_characters_state],
342
- outputs=[current_character, all_characters_state]
343
- )
344
-
345
- for i, (_, _, delete_btn, _) in enumerate(selected_chars_rows):
346
- delete_btn.click(
347
- fn=remove_character,
348
- inputs=[gr.Number(value=i, visible=False), selected_characters],
349
- outputs=[selected_characters, selected_chars_container]
350
- ).then(
351
- fn=update_selected_chars_display,
352
- inputs=[selected_characters],
353
- outputs=[item for row in selected_chars_rows for item in row]
354
- )
355
-
356
- kind.change(
357
- fn=update_gallery,
358
- inputs=[kind, query, all_characters_state],
359
- outputs=[characters, gallery, all_characters_state]
360
- )
361
-
362
- query.change(
363
- fn=update_gallery,
364
- inputs=[kind, query, all_characters_state],
365
- outputs=[characters, gallery, all_characters_state]
366
- )
367
-
368
- with gr.Row():
369
- with gr.Column():
370
- text = gr.Textbox(label=("需要合成的文本"), value="", lines=10, max_lines=10)
371
- inference_button = gr.Button(("🎉 合成语音 🎉"), variant="primary", size='lg')
372
- with gr.Column():
373
- output = gr.Audio(label=("输出的语音"), interactive=False, type="numpy")
374
- cost_time = gr.Textbox(label=("合成时间"), interactive=False, show_label=False, max_lines=1)
375
- try:
376
- inference_button.click(
377
- fn=generate,
378
- inputs=[current_character, selected_characters, text, lang],
379
- outputs=[output, cost_time],
380
- )
381
- except gr.Error as e:
382
- gr.Error(e)
383
- except Exception as e:
384
- pass
385
- gr.Markdown((terms))
386
-
387
- if __name__ == '__main__':
388
- demo.queue(default_concurrency_limit=8).launch(
389
- server_name="0.0.0.0",
390
- server_port=80,
391
- show_api=False
392
- )
 
1
+ from io import StringIO
2
+ import time
3
+ import os
4
+ import logging
5
+
6
+ import gradio as gr
7
+ import pandas as pd
8
+ from pypinyin import lazy_pinyin
9
+
10
+ from api import generate_api
11
+
12
+ # 翻译文件位置
13
+ trans_file = os.path.join(os.path.dirname(__file__),"i18n", "translations.json")
14
+
15
+ # 关闭aiohttp的DEBUG日志
16
+ logging.getLogger('aiohttp').setLevel(logging.WARNING)
17
+ logging.getLogger("gradio").setLevel(logging.WARNING)
18
+
19
+ # 带有时间的log
20
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
21
+
22
+
23
+ terms = r"""
24
+ ## 免责声明
25
+
26
+ 本网站提供的语音合成服务(以下简称“服务”)旨在供个人使用和娱乐目的。在使用本服务前,请用户仔细阅读并充分理解以下条款:
27
+
28
+ 1. **角色版权**:本网站可能使用的角色形象涉及第三方知识产权。本网站不拥有这些角色的版权。用户在使用服务时应尊重相关角色的知识产权,并确保其行为不侵犯任何第三方的知识产权。
29
+
30
+ 2. **用户生成内容(UGC)**:用户通过本平台生成的语音内容(以下简称“UGC”)由用户自行负责,与本平台无关。本平台无法控制或审核用户生成的具体内容,且不对UGC的准确性、完整性或合法性承担任何责任。
31
+
32
+ 3. **使用限制**:本服务生成的语音及其UGC仅限于个人使用,不得用于任何商业目的。未经本平台事先书面同意,禁止将生成内容用于任何商业活动。
33
+
34
+ 4. **法律责任**:用户使用本服务所产生的任何法律责任由用户自行承担,与本平台无关。如因用户使用服务或其UGC导致的任何纠纷或损失,本平台不承担任何责任。
35
+
36
+ 5. **版权声明**:用户应尊重原创,不得使用本服务生成侵犯他人著作权的内容。如发现用户生成内容侵犯他人版权,本平台有权立即停止对其提供服务,并保留追究法律责任的权利。
37
+
38
+ 6. **内容监管**:尽管本平台无法控制UGC,但一旦发现违反本免责声明或法律法规的内容,本平台将采取必要措施,包括但不限于删除违规内容,并配合有关部门进行调查。
39
+
40
+ 7. **注明要求**:用户应在生成内容的显著位置,如可能的话,注明“此内容由RubiiTTS生成”或类似的说明。用户应确保注明行为符合本条款的要求。
41
+
42
+ 用户使用本网站即表示同意以上免责声明。如有疑问,请联系我们contact@yomio.ai。
43
+
44
+ **最终解释权归本网站所有。**
45
+
46
+ """
47
+
48
+ terms_js = r"alert('本网站提供的语音合成服务仅供个人使用和娱乐目的。请注意以下几点:\n1. 角色版权:本网站使用的角色形象可能涉及第三方知识产权,我们不拥有这些角色的版权。\n2. 生成内容:用户通过本平台生成的语音内容由用户自行负责,与本平台无关。我们无法控制或审核用户生成的内容。\n3. 使用限制:生成的语音仅限个人使用,不得用于任何商业目的。\n4. 法律责任:用户使用本服务所产生的任何法律责任由用户自行承担,与本平台无关。\n5. 版权声明:请尊重原创,不要使用本服务生成侵犯他人著作权的内容。\n使用本网站即表示您同意以上免责声明。如有疑问,请联系我们。')"
49
+
50
+ def load_characters_csv(lang):
51
+ name = f"characters_{lang}"
52
+ return pd.read_csv(StringIO(os.getenv(name)))
53
+
54
+ def update_all_characters(lang, current_all_characters):
55
+ new_characters = load_characters_csv(lang)
56
+ initial_characters = get_characters(kind="原神", all_characters=new_characters)
57
+ return new_characters, initial_characters, gr.Gallery(value=[[char['头像'], char['名称']] for char in initial_characters],
58
+ show_label=False, elem_id="character_gallery", columns=[11],
59
+ object_fit="contain", height="auto", interactive=False,
60
+ allow_preview=False, selected_index=None)
61
+
62
+ def get_characters(query=None, page=1, per_page=400, kind="原神", lang="zh", all_characters=None):
63
+ # 使用传入的 all_characters 参数
64
+ filtered_characters = all_characters[all_characters["类别"] == kind]
65
+
66
+ if query:
67
+ # 使用拼音和汉字进行搜索
68
+ filtered_characters = filtered_characters[
69
+ filtered_characters['名称'].str.contains(query, case=False)
70
+ ]
71
+ if filtered_characters.empty and lang == 'zh':
72
+ filtered_characters = all_characters[all_characters["类别"] == kind]
73
+ filtered_characters = filtered_characters[
74
+ filtered_characters['名称'].apply(lambda x: ''.join(lazy_pinyin(x))).str.contains(query, case=False)
75
+ ]
76
+
77
+ # 按名称分组,并选择每组的第一个记录
78
+ unique_characters = filtered_characters.groupby('名称').first().reset_index().sort_values(by='id')
79
+
80
+ # 应用分页
81
+ start_index = (page - 1) * per_page
82
+ end_index = start_index + per_page
83
+
84
+ return unique_characters.iloc[start_index:end_index].to_dict('records')
85
+
86
+ async def generate(selected_character = None, selected_characters = [], text = "", lang="zh"):
87
+ # print("-------",selected_character)
88
+ # print("-------",selected_characters)
89
+ if selected_character:
90
+ characters = [selected_character] + selected_characters
91
+ else:
92
+ characters = selected_characters
93
+ if not selected_character and not selected_characters:
94
+ if lang == "zh":
95
+ raise gr.Error("请先选择一个角色")
96
+ elif lang == "en":
97
+ raise gr.Error("Please select a character first")
98
+ elif lang == "ja":
99
+ raise gr.Error("まず、キャラクターを選択してください")
100
+ elif lang == "ko":
101
+ raise gr.Error("먼저 캐릭터를 선택하세요")
102
+ voice_ids = [char.get("voice_id") for char in characters if char.get("voice_id")]
103
+
104
+ if not voice_ids:
105
+ raise gr.Error("所选角色没有关联的 voice_id")
106
+
107
+ start_time = time.time()
108
+ # 假设我们只使用第一个选择的角色的名称
109
+ if voice_ids == "1":
110
+ if lang == "zh":
111
+ raise gr.Error("该角色暂未创建语音")
112
+ elif lang == "en":
113
+ raise gr.Error("The character has not been created yet")
114
+ elif lang == "ja":
115
+ raise gr.Error("そのキャラクターの音声はまだ作成されていません")
116
+ elif lang == "ko":
117
+ raise gr.Error("해당 캐릭터의 음성이 아직 생성되지 않았습니다")
118
+
119
+ if text == "":
120
+ if lang == "zh":
121
+ raise gr.Error("请输入需要合成的文本")
122
+ elif lang == "en":
123
+ raise gr.Error("Please enter the text to be synthesized")
124
+ elif lang == "ja":
125
+ raise gr.Error("合成するテキストを入力してください")
126
+ elif lang == "ko":
127
+ raise gr.Error("합성할 텍스트를 입력하세요")
128
+
129
+ if (lang == "en" and len(text.split()) > 200) or len(text) > 512:
130
+ if lang == "zh":
131
+ raise gr.Error("长度请控制在512个字符以内")
132
+ elif lang == "en":
133
+ raise gr.Error("The text length exceeds 200 words")
134
+ elif lang == "ja":
135
+ raise gr.Error("テキストの長さが512文字を超えています")
136
+ elif lang == "ko":
137
+ raise gr.Error("텍스트 길이가 512자를 초과합니다")
138
+
139
+ # logging.info(f"选择角色: {characters[0].get('名称')}, 文本: {text}, voice_id: {voice_ids}")
140
+ audio = await generate_api(voice_ids, text)
141
+ end_time = time.time()
142
+ if lang == "zh":
143
+ cost_time = f"合成共花费{end_time - start_time:.2f}秒"
144
+ elif lang == "en":
145
+ cost_time = f"Total time spent synthesizing: {end_time - start_time:.2f} seconds"
146
+ elif lang == "ja":
147
+ cost_time = f"合成にかかった時間: {end_time - start_time:.2f}秒"
148
+ elif lang == "ko":
149
+ cost_time = f"합성에 소요된 시간: {end_time - start_time:.2f}초"
150
+ if isinstance(audio, str):
151
+ print(audio)
152
+ raise gr.Error(audio)
153
+ else:
154
+ return audio, cost_time
155
+
156
+ def get_character_emotions(character, all_characters):
157
+ # all_characters中筛选出与当前角色名称相同的所有记录
158
+ character_records = all_characters[all_characters['名称'] == character['名称']]
159
+
160
+ # 获取所有不重复的情绪
161
+ emotions = character_records['情绪'].unique().tolist()
162
+
163
+ # 如果没有找到情绪,返回一个默认值
164
+ return emotions if emotions else ["默认情绪"]
165
+
166
+ def update_character_info(character_name, emotion, current_character, all_characters):
167
+ character_info = None
168
+ if character_name and emotion:
169
+ character_info = all_characters[(all_characters['名称'] == character_name) & (all_characters['情绪'] == emotion)]
170
+ if character_name == "":
171
+ return None
172
+ character_info = character_info.iloc[0].to_dict()
173
+ return character_info, all_characters
174
+
175
+ def add_new_voice(current_character, selected_characters, kind, lang, all_characters):
176
+ if not current_character:
177
+ if lang == "zh":
178
+ raise gr.Error("请先选择一个角色")
179
+ elif lang == "en":
180
+ raise gr.Error("Please select a character first")
181
+ elif lang == "ja":
182
+ raise gr.Error("まず、キャラクターを選択してください")
183
+ elif lang == "ko":
184
+ raise gr.Error("먼저 캐릭터를 선택하세요")
185
+
186
+ if len(selected_characters) >= 5:
187
+ raise gr.Error("已达到最大选择数(5个)")
188
+
189
+ # 检查是否已存在相同角色
190
+ existing_char = next((char for char in selected_characters if char['名称'] == current_character['名称']), None)
191
+ if existing_char:
192
+ # 如果情绪不同,更新情绪
193
+ if existing_char['情绪'] != current_character['情绪']:
194
+ existing_char['情绪'] = current_character['情绪']
195
+ else:
196
+ selected_characters.insert(0, current_character)
197
+
198
+ updated_characters = get_characters(kind=kind, lang=lang, all_characters=all_characters)
199
+ # ! 取消gallery选中状态,返回个新的gallery是必要的,否则会保留上一次的选中状态。这里sonnet很喜欢改成返回一个数组,但这不能清空gallery的选中状态
200
+ updated_gallery = gr.Gallery(value=[[char['头像'], char['名���']] for char in updated_characters],
201
+ show_label=False, elem_id="character_gallery", columns=[11],
202
+ object_fit="contain", height="auto", interactive=False,
203
+ allow_preview=False, selected_index=None)
204
+
205
+ return (None, gr.update(value=""), gr.update(choices=[]), selected_characters,
206
+ updated_characters, updated_gallery, gr.update(visible=True), all_characters)
207
+
208
+ def update_selected_chars_display(selected_characters):
209
+ updates = []
210
+ for i, (name, emotion, _, row) in enumerate(selected_chars_rows):
211
+ if i < len(selected_characters):
212
+ char = selected_characters[i]
213
+ updates.extend([
214
+ gr.update(value=char['名称'], visible=True),
215
+ gr.update(value=char['情绪'], visible=True),
216
+ gr.update(visible=True),
217
+ gr.update(visible=True)
218
+ ])
219
+ else:
220
+ updates.extend([
221
+ gr.update(value="", visible=False),
222
+ gr.update(value="", visible=False),
223
+ gr.update(visible=False),
224
+ gr.update(visible=False)
225
+ ])
226
+ return updates
227
+
228
+ def remove_character(index, selected_characters):
229
+ if 0 <= index < len(selected_characters):
230
+ del selected_characters[index]
231
+ return selected_characters, gr.update(visible=True)
232
+
233
+ def update_gallery(kind, query, all_characters):
234
+ updated_characters = get_characters(kind=kind, query=query, lang=lang, all_characters=all_characters)
235
+ return updated_characters, [[char['头像'], char['名称']] for char in updated_characters], all_characters
236
+
237
+ def on_select(evt: gr.SelectData, characters, selected_characters, all_characters):
238
+ # 如果没有选择角色,换人的时候清空
239
+ if len(selected_characters) == 0:
240
+ selected_characters = []
241
+
242
+ selected = characters[evt.index]
243
+ emotions = get_character_emotions(selected, all_characters)
244
+ default_emotion = emotions[0] if emotions else ""
245
+
246
+ character_dict = selected.copy()
247
+ character_dict['情绪'] = default_emotion
248
+
249
+ return selected["名称"], gr.Dropdown(choices=emotions, value=default_emotion), character_dict, selected_characters
250
+
251
+ with gr.Blocks(title="Rubii TTS", theme=gr.themes.Soft()) as demo:
252
+ lang = gr.Radio(choices=[("中文", "zh"), ("English", "en"), ("日本語", "ja"), ("한국인", "ko")], label=("Language"), value="zh", scale=1)
253
+ all_characters_state = gr.State(load_characters_csv("zh"))
254
+
255
+ # with Translate(trans_file, lang, placeholder_langs=["en", "zh", "ja", "ko"]):
256
+ gr.Markdown(
257
+ value=("""## 🎉 欢迎使用Rubii语音合成系统 🎉
258
+
259
+ #### [🗣️ 不想只是听到角色的声音,还想与他们进行互动交流吗?快点击我来体验与这些角色的生动对话吧!(中国大陆暂不可用) 🌟](https://rubii.ai)
260
+
261
+ 📝 使用说明:
262
+ 1. 选择角色类别 🎭
263
+ 2. 从图库中选择一个或多个角色(最多5个) 👥。当选择多个角色时,系统会自动进行声线融合(以第一个角色为主音色,其他角色为辅助音色),您可以尝试不同的组合来获得独特的声音效果。
264
+ 3. 选择角色的情绪 😊😢😠
265
+ 4. 输入要合成的文本 ✍️
266
+ 5. 点击"合成语音"按钮 🔊
267
+ """
268
+ ))
269
+ with gr.Group():
270
+ initial_characters = get_characters(kind="原神", lang="zh", all_characters=all_characters_state.value)
271
+ characters = gr.State(initial_characters)
272
+ selected_characters = gr.State([])
273
+ current_character = gr.State(None)
274
+
275
+ with gr.Blocks():
276
+ with gr.Row():
277
+ # kind = gr.Dropdown(choices=["原神", "崩坏星穹铁道","鸣潮","明日方舟","其他"], value="原神", label="请选择角色类别")
278
+ choices = ["原神", "崩坏星穹铁道", "鸣潮"]
279
+ kind = gr.Dropdown(choices=[((name), name) for name in choices], value="原神", label=("选择角色类别"))
280
+ query = gr.Textbox(label=("搜索角色"), value="", lines=1, max_lines=1, interactive=True)
281
+ with gr.Blocks():
282
+ gallery = gr.Gallery(
283
+ value=[[char['头像'], char['名称']] for char in characters.value],
284
+ show_label=False,
285
+ elem_id="character_gallery",
286
+ columns=[11],
287
+ object_fit="contain",
288
+ height="auto",
289
+ interactive=False,
290
+ allow_preview=False,
291
+ selected_index=None
292
+ )
293
+ with gr.Row():
294
+ character_name = gr.Textbox(label=("当前选择的角色"), interactive=False, max_lines=1)
295
+ info_type = gr.Dropdown(choices=[], label=("选择情绪"))
296
+ with gr.Row():
297
+ add_voice_button = gr.Button(("添加新的声音"), variant="primary")
298
+
299
+ selected_chars_container = gr.Column(elem_id="selected_chars_container", visible=False)
300
+
301
+ with selected_chars_container:
302
+ gr.Markdown(("### 已选择的角色"))
303
+ selected_chars_rows = []
304
+ for i in range(5): # 假设最多选择5个角色
305
+ with gr.Row() as row:
306
+ name = gr.Textbox(label=("名称"), interactive=False, max_lines=1)
307
+ emotion = gr.Textbox(label=("情绪"), interactive=False, max_lines=1)
308
+ delete_btn = gr.Button(("删除"), scale=0)
309
+ selected_chars_rows.append((name, emotion, delete_btn, row))
310
+
311
+
312
+ # -------------- 绑定事件 --------------
313
+
314
+ lang.change(
315
+ fn=update_all_characters,
316
+ inputs=[lang, all_characters_state],
317
+ outputs=[all_characters_state, characters, gallery]
318
+ )
319
+
320
+ add_voice_button.click(
321
+ fn=add_new_voice,
322
+ inputs=[current_character, selected_characters, kind, lang, all_characters_state],
323
+ outputs=[current_character, character_name, info_type, selected_characters,
324
+ characters, gallery, selected_chars_container, all_characters_state]
325
+ ).then(
326
+ fn=update_selected_chars_display,
327
+ inputs=[selected_characters],
328
+ outputs=[item for row in selected_chars_rows for item in row]
329
+ )
330
+
331
+
332
+ gallery.select(
333
+ fn=on_select,
334
+ inputs=[characters, selected_characters, all_characters_state],
335
+ outputs=[character_name, info_type, current_character, selected_characters]
336
+ )
337
+
338
+ info_type.change(
339
+ fn=update_character_info,
340
+ inputs=[character_name, info_type, current_character, all_characters_state],
341
+ outputs=[current_character, all_characters_state]
342
+ )
343
+
344
+ for i, (_, _, delete_btn, _) in enumerate(selected_chars_rows):
345
+ delete_btn.click(
346
+ fn=remove_character,
347
+ inputs=[gr.Number(value=i, visible=False), selected_characters],
348
+ outputs=[selected_characters, selected_chars_container]
349
+ ).then(
350
+ fn=update_selected_chars_display,
351
+ inputs=[selected_characters],
352
+ outputs=[item for row in selected_chars_rows for item in row]
353
+ )
354
+
355
+ kind.change(
356
+ fn=update_gallery,
357
+ inputs=[kind, query, all_characters_state],
358
+ outputs=[characters, gallery, all_characters_state]
359
+ )
360
+
361
+ query.change(
362
+ fn=update_gallery,
363
+ inputs=[kind, query, all_characters_state],
364
+ outputs=[characters, gallery, all_characters_state]
365
+ )
366
+
367
+ with gr.Row():
368
+ with gr.Column():
369
+ text = gr.Textbox(label=("需要合成的文本"), value="", lines=10, max_lines=10)
370
+ inference_button = gr.Button(("🎉 合成语音 🎉"), variant="primary", size='lg')
371
+ with gr.Column():
372
+ output = gr.Audio(label=("输出的语音"), interactive=False, type="numpy")
373
+ cost_time = gr.Textbox(label=("合成时间"), interactive=False, show_label=False, max_lines=1)
374
+ try:
375
+ inference_button.click(
376
+ fn=generate,
377
+ inputs=[current_character, selected_characters, text, lang],
378
+ outputs=[output, cost_time],
379
+ )
380
+ except gr.Error as e:
381
+ gr.Error(e)
382
+ except Exception as e:
383
+ pass
384
+ gr.Markdown((terms))
385
+
386
+ if __name__ == '__main__':
387
+ demo.queue(default_concurrency_limit=8).launch(
388
+ server_name="0.0.0.0",
389
+ server_port=80,
390
+ show_api=False
391
+ )