v1
Browse files- .gitignore +162 -0
- api.py +25 -0
- app.py +392 -0
- i18n/emotion.json +46 -0
- i18n/translate.py +63 -0
- i18n/translations.json +86 -0
- requirements.txt +4 -0
.gitignore
ADDED
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Byte-compiled / optimized / DLL files
|
2 |
+
__pycache__/
|
3 |
+
*.py[cod]
|
4 |
+
*$py.class
|
5 |
+
|
6 |
+
# C extensions
|
7 |
+
*.so
|
8 |
+
|
9 |
+
# Distribution / packaging
|
10 |
+
.Python
|
11 |
+
build/
|
12 |
+
develop-eggs/
|
13 |
+
dist/
|
14 |
+
downloads/
|
15 |
+
eggs/
|
16 |
+
.eggs/
|
17 |
+
lib/
|
18 |
+
lib64/
|
19 |
+
parts/
|
20 |
+
sdist/
|
21 |
+
var/
|
22 |
+
wheels/
|
23 |
+
share/python-wheels/
|
24 |
+
*.egg-info/
|
25 |
+
.installed.cfg
|
26 |
+
*.egg
|
27 |
+
MANIFEST
|
28 |
+
|
29 |
+
# PyInstaller
|
30 |
+
# Usually these files are written by a python script from a template
|
31 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
32 |
+
*.manifest
|
33 |
+
*.spec
|
34 |
+
|
35 |
+
# Installer logs
|
36 |
+
pip-log.txt
|
37 |
+
pip-delete-this-directory.txt
|
38 |
+
|
39 |
+
# Unit test / coverage reports
|
40 |
+
htmlcov/
|
41 |
+
.tox/
|
42 |
+
.nox/
|
43 |
+
.coverage
|
44 |
+
.coverage.*
|
45 |
+
.cache
|
46 |
+
nosetests.xml
|
47 |
+
coverage.xml
|
48 |
+
*.cover
|
49 |
+
*.py,cover
|
50 |
+
.hypothesis/
|
51 |
+
.pytest_cache/
|
52 |
+
cover/
|
53 |
+
|
54 |
+
# Translations
|
55 |
+
*.mo
|
56 |
+
*.pot
|
57 |
+
|
58 |
+
# Django stuff:
|
59 |
+
*.log
|
60 |
+
local_settings.py
|
61 |
+
db.sqlite3
|
62 |
+
db.sqlite3-journal
|
63 |
+
|
64 |
+
# Flask stuff:
|
65 |
+
instance/
|
66 |
+
.webassets-cache
|
67 |
+
|
68 |
+
# Scrapy stuff:
|
69 |
+
.scrapy
|
70 |
+
|
71 |
+
# Sphinx documentation
|
72 |
+
docs/_build/
|
73 |
+
|
74 |
+
# PyBuilder
|
75 |
+
.pybuilder/
|
76 |
+
target/
|
77 |
+
|
78 |
+
# Jupyter Notebook
|
79 |
+
.ipynb_checkpoints
|
80 |
+
|
81 |
+
# IPython
|
82 |
+
profile_default/
|
83 |
+
ipython_config.py
|
84 |
+
|
85 |
+
# pyenv
|
86 |
+
# For a library or package, you might want to ignore these files since the code is
|
87 |
+
# intended to run in multiple environments; otherwise, check them in:
|
88 |
+
# .python-version
|
89 |
+
|
90 |
+
# pipenv
|
91 |
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
92 |
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
93 |
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
94 |
+
# install all needed dependencies.
|
95 |
+
#Pipfile.lock
|
96 |
+
|
97 |
+
# poetry
|
98 |
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
99 |
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
100 |
+
# commonly ignored for libraries.
|
101 |
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
102 |
+
#poetry.lock
|
103 |
+
|
104 |
+
# pdm
|
105 |
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
106 |
+
#pdm.lock
|
107 |
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
108 |
+
# in version control.
|
109 |
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
110 |
+
.pdm.toml
|
111 |
+
.pdm-python
|
112 |
+
.pdm-build/
|
113 |
+
|
114 |
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
115 |
+
__pypackages__/
|
116 |
+
|
117 |
+
# Celery stuff
|
118 |
+
celerybeat-schedule
|
119 |
+
celerybeat.pid
|
120 |
+
|
121 |
+
# SageMath parsed files
|
122 |
+
*.sage.py
|
123 |
+
|
124 |
+
# Environments
|
125 |
+
.env
|
126 |
+
.venv
|
127 |
+
env/
|
128 |
+
venv/
|
129 |
+
ENV/
|
130 |
+
env.bak/
|
131 |
+
venv.bak/
|
132 |
+
|
133 |
+
# Spyder project settings
|
134 |
+
.spyderproject
|
135 |
+
.spyproject
|
136 |
+
|
137 |
+
# Rope project settings
|
138 |
+
.ropeproject
|
139 |
+
|
140 |
+
# mkdocs documentation
|
141 |
+
/site
|
142 |
+
|
143 |
+
# mypy
|
144 |
+
.mypy_cache/
|
145 |
+
.dmypy.json
|
146 |
+
dmypy.json
|
147 |
+
|
148 |
+
# Pyre type checker
|
149 |
+
.pyre/
|
150 |
+
|
151 |
+
# pytype static type analyzer
|
152 |
+
.pytype/
|
153 |
+
|
154 |
+
# Cython debug symbols
|
155 |
+
cython_debug/
|
156 |
+
|
157 |
+
# PyCharm
|
158 |
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
159 |
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
160 |
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
161 |
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
162 |
+
#.idea/
|
api.py
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import asyncio
|
2 |
+
import aiohttp
|
3 |
+
import io
|
4 |
+
import os
|
5 |
+
|
6 |
+
BASE_URL = os.getenv("BASE_URL")
|
7 |
+
|
8 |
+
async def generate_api(voice_ids, text):
|
9 |
+
timeout = aiohttp.ClientTimeout(total=10) # 设置10秒的总超时时间
|
10 |
+
try:
|
11 |
+
async with aiohttp.ClientSession(timeout=timeout) as session:
|
12 |
+
async with session.post(BASE_URL+"tts", json={"ids": voice_ids, "text": text}) as response:
|
13 |
+
if response.status == 200:
|
14 |
+
# 读取响应内容
|
15 |
+
audio_data = await response.read()
|
16 |
+
# print(type(audio_data))
|
17 |
+
# 创建一个字节流对象
|
18 |
+
return audio_data
|
19 |
+
else:
|
20 |
+
print(response)
|
21 |
+
return f"合成失败: {response.status}"
|
22 |
+
except asyncio.TimeoutError:
|
23 |
+
return "请求超时,请稍后重试"
|
24 |
+
except aiohttp.ClientError as e:
|
25 |
+
return f"网络错误: {str(e)}"
|
app.py
ADDED
@@ -0,0 +1,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 |
+
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 |
+
)
|
i18n/emotion.json
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"zh": {
|
3 |
+
"正常": "正常",
|
4 |
+
"生气": "生气",
|
5 |
+
"悲伤": "悲伤",
|
6 |
+
"惊讶": "惊讶",
|
7 |
+
"害怕": "害怕",
|
8 |
+
"厌恶": "厌恶",
|
9 |
+
"开心": "开心",
|
10 |
+
"失望": "失望",
|
11 |
+
"紧张": "紧张"
|
12 |
+
},
|
13 |
+
"en": {
|
14 |
+
"正常": "normal",
|
15 |
+
"生气": "angry",
|
16 |
+
"悲伤": "sad",
|
17 |
+
"惊讶": "surprise",
|
18 |
+
"害怕": "fear",
|
19 |
+
"厌恶": "disgust",
|
20 |
+
"开心": "happy",
|
21 |
+
"失望": "disappointment",
|
22 |
+
"紧张": "nervous"
|
23 |
+
},
|
24 |
+
"ja": {
|
25 |
+
"正常": "正常",
|
26 |
+
"生气": "怒り",
|
27 |
+
"悲伤": "悲しい",
|
28 |
+
"惊讶": "驚き",
|
29 |
+
"害怕": "恐れ",
|
30 |
+
"厌恶": "嫌悪",
|
31 |
+
"开心": "嬉しい",
|
32 |
+
"失望": "失望",
|
33 |
+
"紧张": "緊張"
|
34 |
+
},
|
35 |
+
"ko": {
|
36 |
+
"正常": "보통",
|
37 |
+
"生气": "화남",
|
38 |
+
"悲伤": "슬픔",
|
39 |
+
"惊讶": "놀람",
|
40 |
+
"害怕": "두려움",
|
41 |
+
"厌恶": "혐오",
|
42 |
+
"开心": "행복",
|
43 |
+
"失望": "실망",
|
44 |
+
"紧张": "긴장"
|
45 |
+
}
|
46 |
+
}
|
i18n/translate.py
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import pandas as pd
|
3 |
+
from typing import Literal
|
4 |
+
import os
|
5 |
+
import shutil
|
6 |
+
|
7 |
+
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
8 |
+
|
9 |
+
class LanguageConvertor:
|
10 |
+
def __init__(self, lang: Literal["en", "ja", "ko"]):
|
11 |
+
self.lang = lang
|
12 |
+
self.emotion = json.load(open("emotion.json", "r", encoding="utf-8"))[lang]
|
13 |
+
self.data = pd.read_csv(f"characters_{self.lang}.csv")
|
14 |
+
|
15 |
+
def get_name(self):
|
16 |
+
names = self.data["名称"].tolist()
|
17 |
+
return [name for i, name in enumerate(names) if name not in names[:i]]
|
18 |
+
|
19 |
+
def replace_emotion(self):
|
20 |
+
emotion = self.emotion
|
21 |
+
for i, row in self.data.iterrows():
|
22 |
+
try:
|
23 |
+
self.data.loc[i, "情绪"] = emotion[row["情绪"]]
|
24 |
+
except KeyError:
|
25 |
+
pass
|
26 |
+
|
27 |
+
def generate_name_template(self, names: set[str]):
|
28 |
+
res = "{\n"
|
29 |
+
for i, name in enumerate(names):
|
30 |
+
if i == len(names) - 1:
|
31 |
+
res += f" \"{name}\": \"{name}\"\n"
|
32 |
+
else:
|
33 |
+
res += f" \"{name}\": \"{name}\",\n"
|
34 |
+
with open(os.path.join("temp", f"translate_name_{self.lang}.json"), "w", encoding="utf-8") as f:
|
35 |
+
f.write(res + "}")
|
36 |
+
|
37 |
+
def replace_name(self):
|
38 |
+
with open(os.path.join("temp", f"translate_name_{self.lang}.json"), "r", encoding="utf-8") as f:
|
39 |
+
translate_name = json.load(f)
|
40 |
+
for i, row in self.data.iterrows():
|
41 |
+
self.data.loc[i, "名称"] = translate_name[row["名称"]]
|
42 |
+
|
43 |
+
def to_file(self):
|
44 |
+
self.data.to_csv(f"characters_{self.lang}.csv", index=False)
|
45 |
+
|
46 |
+
if __name__ == "__main__":
|
47 |
+
# step = 1为生成翻译模板
|
48 |
+
# step = 2为生成翻译后的角色文件
|
49 |
+
# 请确保第一步已完成再进行第二步
|
50 |
+
|
51 |
+
step = 2
|
52 |
+
convertors = [LanguageConvertor(lang) for lang in ["en", "ja", "ko"]]
|
53 |
+
os.makedirs("temp", exist_ok=True)
|
54 |
+
for convertor in convertors:
|
55 |
+
convertor.replace_emotion()
|
56 |
+
print(convertor.data)
|
57 |
+
if step == 1:
|
58 |
+
convertor.generate_name_template(convertor.get_name())
|
59 |
+
if step == 2:
|
60 |
+
for convertor in convertors:
|
61 |
+
convertor.replace_name()
|
62 |
+
convertor.to_file()
|
63 |
+
# shutil.rmtree("temp")
|
i18n/translations.json
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"en": {
|
3 |
+
"Language": "Language",
|
4 |
+
"## 🎉 欢迎使用Rubii语音合成系统 🎉\n \n#### [🗣️ 不想只是听到角色的声音,还想与他们进行互动交流吗?快点击我来体验与这些角色的生动对话吧!(中国大陆暂不可用) 🌟](https://rubii.ai)\n\n📝 使用说明:\n1. 选择角色类别 🎭\n2. 从图库中选择一个或多个角色(最多5个) 👥。当选择多个角色时,系统会自动进行声线融合(以第一个角色为主音色,其他角色为辅助音色),您可以尝试不同的组合来获得独特的声音效果。\n3. 选择角色的情绪 😊😢😠\n4. 输入要合成的文本 ✍️\n5. 点击\"合成语音\"按钮 🔊": "## 🎉 Welcome to Rubii Voice Synthesis System 🎉\n \n#### [🗣️ Don't just want to hear the character's voice, but also want to interact with them? Click me to experience vivid conversations with these characters! (Not available in mainland China) 🌟](https://rubii.ai)\n\n📝 Instructions:\n1. Select character category 🎭\n2. Choose one or more characters from the gallery (up to 5) 👥. When multiple characters are selected, the system will automatically blend the voices (the first character will be the main voice, and the other characters will be auxiliary voices). You can try different combinations to get unique voice effects.\n3. Select the character's emotion 😊😢😠\n4. Enter the text to be synthesized ✍️\n5. Click the \"Synthesize Speech\" button 🔊",
|
5 |
+
"原神": "Genshin Impact",
|
6 |
+
"崩坏星穹铁道": "Honkai: Star Rail",
|
7 |
+
"鸣潮": "Wuthering Waves",
|
8 |
+
"选择角色类别": "Select character category",
|
9 |
+
"搜索角色": "Search character",
|
10 |
+
"当前选择的角色": "Currently selected character",
|
11 |
+
"选择情绪": "Select emotion",
|
12 |
+
"添加新的声音": "Add new voice",
|
13 |
+
"### 已选择的角色": "### Selected characters",
|
14 |
+
"名称": "Name",
|
15 |
+
"情绪": "Emotion",
|
16 |
+
"删除": "Delete",
|
17 |
+
"需要合成的文本": "Text to synthesize",
|
18 |
+
"🎉 合成语音 🎉": "🎉 Synthesize Speech 🎉",
|
19 |
+
"输出的语音": "Output audio",
|
20 |
+
"合成时间": "Synthesis time",
|
21 |
+
"## 免责声明\n\n本网站提供的语音合成服务(以下简称“服务”)旨在供个人使用和娱乐目的。在使用本服务前,请用户仔细阅读并充分理解以下条款:\n\n1. **角色版权**:本网站可能使用的角色形象涉及第三方知识产权。本网站不拥有这些角色的版权。用户在使用服务时应尊重相关角色的知识产权,并确保其行为不侵犯任何第三方的知识产权。\n\n2. **用户生成内容(UGC)**:用户通过本平台生成的语音内容(以下简称“UGC”)由用户自行负责,与本平台无关。本平台无法控制或审核用户生成的具体内容,且不对UGC的准确性、完整性或合法性承担任何责任。\n\n3. **使用限制**:本服务生成的语音及其UGC仅限于个人使用,不得用于任何商业目的。未经本平台事先书面同意,禁止将生成内容用于任何商业活动。\n\n4. **法律责任**:用户使用本服务所产生的任何法律责任由用户自行承担,与本平台无关。如因用户使用服务或其UGC导致的任何纠纷或损失,本平台不承担任何责任。\n\n5. **版权声明**:用户应尊重原创,不得使用本服务生成侵犯他人著作权的内容。如发现用户生成内容侵犯他人版权,本平台有权立即停止对其提供服务,并保留追究法律责任的权利。\n\n6. **内容监管**:尽管本平台无法控制UGC,但一旦发现违反本免责声明或法律法规的内容,本平台将采取必要措施,包括但不限于删除违规内容,并配合有关部门进行调查。\n\n7. **注明要求**:用户应在生成内容的显著位置,如可能的话,注明“此内容由RubiiTTS生成”或类似的说明。用户应确保注明行为符合本条款的要求。\n\n用户使用本网站即表示同意以上免责声明。如有疑问,请联系我们contact@yomio.ai。\n\n**最终解释权归本网站所有。**": "## Disclaimer\n\nThe voice synthesis service provided by this website (hereinafter referred to as the \"Service\") is intended for personal use and entertainment purposes. Before using this Service, please carefully read and fully understand the following terms:\n\n1. **Character Copyright**: The character images used on this website may involve third-party intellectual property rights. This website does not own the copyrights to these characters. Users should respect the intellectual property rights of the relevant characters when using the Service and ensure that their actions do not infringe upon any third-party intellectual property rights.\n\n2. **User-Generated Content (UGC)**: The voice content generated by users through this platform (hereinafter referred to as \"UGC\") is the sole responsibility of the users and is not related to this platform. This platform cannot control or review the specific content generated by users and does not assume any responsibility for the accuracy, completeness, or legality of UGC.\n\n3. **Usage Restrictions**: The voices and their UGC generated by this Service are limited to personal use only and may not be used for any commercial purposes. It is prohibited to use the generated content for any commercial activities without prior written consent from this platform.\n\n4. **Legal Responsibility**: Any legal responsibility arising from the use of this Service by users shall be borne by the users themselves and is not related to this platform. This platform does not assume any responsibility for any disputes or losses caused by users' use of the Service or their UGC.\n\n5. **Copyright Statement**: Users should respect original creations and must not use this Service to generate content that infringes upon others' copyrights. If user-generated content is found to infringe upon others' copyrights, this platform reserves the right to immediately cease providing services to them and reserves the right to pursue legal action.\n\n6. **Content Moderation**: Although this platform cannot control UGC, once content that violates this disclaimer or laws and regulations is discovered, this platform will take necessary measures, including but not limited to deleting the violating content and cooperating with relevant authorities in investigations.\n\n7. **Attribution Requirement**: Users should, where possible, prominently indicate \"This content was generated by RubiiTTS\" or a similar statement in the generated content. Users should ensure that the attribution complies with the requirements of these terms.\n\nBy using this website, users agree to the above disclaimer. If you have any questions, please contact us at contact@yomio.ai.\n\n**The final interpretation right belongs to this website.**"
|
22 |
+
},
|
23 |
+
"zh": {
|
24 |
+
"Language": "语言",
|
25 |
+
"## 🎉 欢迎使用Rubii语音合成系统 🎉\n \n#### [🗣️ 不想只是听到角色的声音,还想与他们进行互动交流吗?快点击我来体验与这些角色的生动对话吧!(中国大陆暂不可用) 🌟](https://rubii.ai)\n\n📝 使用说明:\n1. 选择角色类别 🎭\n2. 从图库中选择一个或多个角色(最多5个) 👥。当选择多个角色时,系统会自动进行声线融合(以第一个角色为主音色,其他角色为辅助音色),您可以尝试不同的组合来获得独特的声音效果。\n3. 选择角色的情绪 😊😢😠\n4. 输入要合成的文本 ✍️\n5. 点击\"合成语音\"按钮 🔊": "## 🎉 欢迎使用Rubii语音合成系统 🎉\n \n#### [🗣️ 不想只是听到角色的声音,还想与他们进行互动交流吗?快点击我来体验与这些角色的生动对话吧!(中国大陆暂不可用) 🌟](https://rubii.ai)\n\n📝 使用说明:\n1. 选择角色类别 🎭\n2. 从图库中选择一个或多个角色(最多5个) 👥。当选择多个角色时,系统会自动进行声线融合(以第一个角色为主音色,其他角色为辅助音色),您可以尝试不同的组合来获得独特的声音效果。\n3. 选择角色的情绪 😊😢😠\n4. 输入要合成的文本 ✍️\n5. 点击\"合成语音\"按钮 🔊",
|
26 |
+
"原神": "原神",
|
27 |
+
"崩坏星穹铁道": "崩坏星穹铁道",
|
28 |
+
"鸣潮": "鸣潮",
|
29 |
+
"选择角色类别": "选择角色类别",
|
30 |
+
"搜索角色": "搜索角色",
|
31 |
+
"当前选择的角色": "当前选择的角色",
|
32 |
+
"选择情绪": "选择情绪",
|
33 |
+
"添加新的声音": "添加新的声音",
|
34 |
+
"### 已选择的角色": "### 已选择的角色",
|
35 |
+
"名称": "名称",
|
36 |
+
"情绪": "情绪",
|
37 |
+
"删除": "删除",
|
38 |
+
"需要合成的文本": "需要合成的文本",
|
39 |
+
"🎉 合成语音 🎉": "🎉 合成语音 🎉",
|
40 |
+
"输出的语音": "输出的语音",
|
41 |
+
"合成时间": "合成时间",
|
42 |
+
"## 免责声明\n\n本网站提供的语音合成服务(以下简称“服务”)旨在供个人使用和娱乐目的。在使用本服务前,请用户仔细阅读并充分理解以下条款:\n\n1. **角色版权**:本网站可能使用的角色形象涉及第三方知识产权。本网站不拥有这些角色的版权。用户在使用服务时应尊重相关角色的知识产权,并确保其行为不侵犯任何第三方的知识产权。\n\n2. **用户生成内容(UGC)**:用户通过本平台生成的语音内容(以下简称“UGC”)由用户自行负责,与本平台无关。本平台无法控制或审核用户生成的具体内容,且不对UGC的准确性、完整性或合法性承担任何责任。\n\n3. **使用限制**:本服务生成的语音及其UGC仅限于个人使用,不得用于任何商业目的。未经本平台事先书面同意,禁止将生成内容用于任何商业活动。\n\n4. **法律责任**:用户使用本服务所产生的任何法律责任由用户自行承担,与本平台无关。如因用户使用服务或其UGC导致的任何纠纷或损失,本平台不承担任何责任。\n\n5. **版权声明**:用户���尊重原创,不得使用本服务生成侵犯他人著作权的内容。如发现用户生成内容侵犯他人版权,本平台有权立即停止对其提供服务,并保留追究法律责任的权利。\n\n6. **内容监管**:尽管本平台无法控制UGC,但一旦发现违反本免责声明或法律法规的内容,本平台将采取必要措施,包括但不限于删除违规内容,并配合有关部门进行调查。\n\n7. **注明要求**:用户应在生成内容的显著位置,如可能的话,注明“此内容由RubiiTTS生成”或类似的说明。用户应确保注明行为符合本条款的要求。\n\n用户使用本网站即表示同意以上免责声明。如有疑问,请联系我们contact@yomio.ai。\n\n**最终解释权归本网站所有。**": "## 免责声明\n\n本网站提供的语音合成服务(以下简称“服务”)旨在供个人使用和娱乐目的。在使用本服务前,请用户仔细阅读并充分理解以下条款:\n\n1. **角色版权**:本网站可能使用的角色形象涉及第三方知识产权。本网站不拥有这些角色的版权。用户在使用服务时应尊重相关角色的知识产权,并确保其行为不侵犯任何第三方的知识产权。\n\n2. **用户生成内容(UGC)**:用户通过本平台生成的语音内容(以下简称“UGC”)由用户自行负责,与本平台无关。本平台无法控制或审核用户生成的具体内容,且不对UGC的准确性、完整性或合法性承担任何责任。\n\n3. **使用限制**:本服务生成的语音及其UGC仅限于个人使用,不得用于任何商业目的。未经本平台事先书面同意,禁止将生成内容用于任何商业活动。\n\n4. **法律责任**:用户使用本服务所产生的任何法律责任由用户自行承担,与本平台无关。如因用户使用服务或其UGC导致的任何纠纷或损失,本平台不承担任何责任。\n\n5. **版权声明**:用户应尊重原创,不得使用本服务生成侵犯他人著作权的内容。如发现用户生成内容侵犯他人版权,本平台有权立即停止对其提供服务,并保留追究法律责任的权利。\n\n6. **内容监管**:尽管本平台无法控制UGC,但一旦发现违反本免责声明或法律法规的内容,本平台将采取必要措施,包括但不限于删除违规内容,并配合有关部门进行调查。\n\n7. **注明要求**:用户应在生成内容的显著位置,如可能的话,注明“此内容由RubiiTTS生成”或类似的说明。用户应确保注明行为符合本条款的要求。\n\n用户使用本网站即表示同意以上免责声明。如有疑问,请联系我们contact@yomio.ai。\n\n**最终解释权归本网站所有。**"
|
43 |
+
},
|
44 |
+
"ja": {
|
45 |
+
"Language": "Language",
|
46 |
+
"## 🎉 欢迎使用Rubii语音合成系统 🎉\n \n#### [🗣️ 不想只是听到角色的声音,还想与他们进行互动交流吗?快点击我来体验与这些角色的生动对话吧!(中国大陆暂不可用) 🌟](https://rubii.ai)\n\n📝 使用说明:\n1. 选择角色类别 🎭\n2. 从图库中选择一个或多个角色(最多5个) 👥。当选择多个角色时,系统会自动进行声线融合(以第一个角色为主音色,其他角色为辅助音色),您可以尝试不同的组合来获得独特的声音效果。\n3. 选择角色的情绪 😊😢😠\n4. 输入要合成的文本 ✍️\n5. 点击\"合成语音\"按钮 🔊": "## 🎉 Rubii音声合成システムへようこそ 🎉\n \n#### [🗣️ キャラクターの声を聞くだけでなく、彼らと対話したいですか? クリックして、これらのキャラクターとの生き生きとした会話を体験してください! (中国本土では利用できません) 🌟](https://rubii.ai)\n\n📝 使用説明:\n1. キャラクターカテゴリーを選択 🎭\n2. ギャラリーから1人以上のキャラクターを選択 (最大5人) 👥。複数のキャラクターを選択すると、システムは自動的に声をブレンドします (最初のキャラクターがメインの声になり、他のキャラクターが補助の声になります)。さまざまな組み合わせを試して、ユニークな声の効果を得ることができます。\n3. キャラクターの感情を選択 😊😢😠\n4. 合成するテキストを入力 ✍️\n5. 「音声を合成」ボタンをクリック 🔊",
|
47 |
+
"原神": "原神[げんしん",
|
48 |
+
"崩坏星穹铁道": "崩壊:スターレイル",
|
49 |
+
"鸣潮": "Wuthering Waves",
|
50 |
+
"选择角色类别": "キャラクターカテゴリーを選択",
|
51 |
+
"搜索角色": "キャラクターを検索",
|
52 |
+
"当前选择的角色": "現在選択されているキャラクター",
|
53 |
+
"选择情绪": "感情を選択",
|
54 |
+
"添加新的声音": "新しい声を追加",
|
55 |
+
"### 已选择的角色": "### 選択されたキャラクター",
|
56 |
+
"名称": "名前",
|
57 |
+
"情绪": "感情",
|
58 |
+
"删除": "削除",
|
59 |
+
"需要合成的文本": "合成するテキスト",
|
60 |
+
"🎉 合成语音 🎉": "🎉 音声を合成 🎉",
|
61 |
+
"输出的语音": "出力された音声",
|
62 |
+
"合成时间": "合成時間",
|
63 |
+
"## 免责声明\n\n本网站提供的语音合成服务(以下简称“服务”)旨在供个人使用和娱乐目的。在使用本服务前,请用户仔细阅读并充分理解以下条款:\n\n1. **角色版权**:本网站可能使用的角色形象涉及第三方知识产权。本网站不拥有这些角色的版权。用户在使用服务时应尊重相关角色的知识产权,并确保其行为不侵犯任何第三方的知识产权。\n\n2. **用户生成内容(UGC)**:用户通过本平台生成的语音内容(以下简称“UGC”)由用户自行负责,与本平台无关。本平台无法控制或审核用户生成的具体内容,且不对UGC的准确性、完整性或合法性承担任何责任。\n\n3. **使用限制**:本服务生成的语音及其UGC仅限于个人使用,不得用于任何商业目的。未经本平台事先书面同意,禁止将生成内容用于任何商业活动。\n\n4. **法律责任**:用户使用本服务所产生的任何法律责任由用户自行承担,与本平台无关。如因用户使用服务或其UGC导致的任何纠纷或损失,本平台不承担任何责任。\n\n5. **版权声明**:用户应尊重原创,不得使用本服务生成侵犯他人著作权的内容。如发现用户生成内容侵犯他人版权,本平台有权立即停止对其提供服务,并保留追究法律责任的权利。\n\n6. **内容监管**:尽管本平台无法控制UGC,但一旦发现违反本免责声明或法律法规的内容,本平台将采取必要措施,包括但不限于删除违规内容,并配合有关部门进行调查。\n\n7. **注明要求**:用户应在生成内容的显著位置,如可能的话,注明“此内容由RubiiTTS生成”或类似的说明。用户应确保注明行为符合本条款的要求。\n\n用户使用本网站即表示同意以上免责声明。如有疑问,请联系我们contact@yomio.ai。\n\n**最终解释权归本网站所有。**": "## 免責事項\n\n本ウェブサイトが提供する音声合成サービス(以下「サービス」という)は、個人使用および娯楽目的のために提供されています。本サービスをご利用になる前に、以下の条項を注意深くお読みいただき、十分にご理解ください:\n\n1. **キャラクターの著作権**:本ウェブサイトで使用されているキャラクターイメージは、第三者の知的財産権に関わる可能性があります。本ウェブサイトはこれらのキャラクターの著作権を所有していません。ユーザーはサービスを利用する際、関連するキャラクターの知的財産権を尊重し、第三者の知的財産権を侵害しないよう確保する必要があります。\n\n2. **ユーザー生成コンテンツ(UGC)**:ユーザーが本プラットフォームを通じて生成した音声コンテンツ(以下「UGC」という)は、ユーザー自身の責任であり、本プラットフォームとは無関係です。本プラットフォームはユーザーが生成した具体的なコンテンツを制御または審査することはできず、UGCの正確性、完全性、または合法性について一切の責任を負いません。\n\n3. **使用制限**:本サービスで生成された音声およびそのUGCは個人使用に限定され、商業目的での使用は禁止されています。本プラットフォームの事前の書面による同意なしに、生成されたコンテンツを商業活動に使用することは禁止されています。\n\n4. **法的責任**:ユーザーが本サービスを利用することによって生じるいかなる法的責任も、ユーザー自身が負うものとし、本プラットフォームとは無関係です。ユーザーのサービス利用またはそのUGCに起因するいかなる紛争や損失についても、本プラットフォームは一切の責任を負いません。\n\n5. **著作権声明**:ユーザーは創作物を尊重し、他人の著作権を侵害するコンテンツを本サービスで生成してはいけません。ユーザーが生成したコンテンツが他人の著作権を侵害していることが発見された場合、本プラットフォームはただちにサービスの提供を停止する権利を有し、法的責任を追及する権利を留保します。\n\n6. **コンテンツ監視**:本プラットフォームはUGCを制御できませんが、本免責事項や法律規制に違反するコンテンツが発見された場合、本プラットフォームは必要な措置を講じます。これには違反コンテンツの削除や関係機関との調査協力が含まれますが、これらに限定されません。\n\n7. **表示要件**:ユーザーは生成されたコンテンツの目立つ��置に、可能な限り「このコンテンツはRubiiTTSによって生成されました」または類似の説明を表示する必要があります。ユーザーはこの表示行為が本条項の要件に適合していることを確認する必要があります。\n\nユーザーが本ウェブサイトを利用することは、上記の免責事項に同意したことを意味します。ご質問がある場合は、contact@yomio.aiまでお問い合わせください。\n\n**最終的な解釈権は本ウェブサイトに帰属します。**"
|
64 |
+
},
|
65 |
+
"ko": {
|
66 |
+
"Language": "Language",
|
67 |
+
"## 🎉 欢迎使用Rubii语音合成系统 🎉\n \n#### [🗣️ 不想只是听到角色的声音,还想与他们进行互动交流吗?快点击我来体验与这些角色的生动对话吧!(中国大陆暂不可用) 🌟](https://rubii.ai)\n\n📝 使用说明:\n1. 选择角色类别 🎭\n2. 从图库中选择一个或多个角色(最多5个) 👥。当选择多个角色时,系统会自动进行声线融合(以第一个角色为主音色,其他角色为辅助音色),您可以尝试不同的组合来获得独特的声音效果。\n3. 选择角色的情绪 😊😢😠\n4. 输入要合成的文本 ✍️\n5. 点击\"合成语音\"按钮 🔊": "## 🎉 Rubii 음성 합성 시스템에 오신 것을 환영합니다 🎉\n \n#### [🗣️ 캐릭터의 목소리를 듣는 것뿐만 아니라 그들과 상호작용하고 싶으신가요? 클릭하여 이 캐릭터들과 생동감 넘치는 대화를 경험해보세요! (중국 본토에서는 사용 불가) 🌟](https://rubii.ai)\n\n📝 사용 설명서:\n1. 캐릭터 카테고리 선택 🎭\n2. 갤러리에서 하나 이상의 캐릭터 선택 (최대 5개) 👥. 여러 캐릭터를 선택하면 시스템이 자동으로 음성을 혼합합니다 (첫 번째 캐릭터가 주 음색이 되고, 다른 캐릭터는 보조 음색이 됩니다). 다양한 조합을 시도하여 독특한 음성 효과를 얻을 수 있습니다.\n3. 캐릭터의 감정 선택 😊😢😠\n4. 합성할 텍스트 입력 ✍️\n5. \"음성 합성\" 버튼 클릭 🔊",
|
68 |
+
"原神": "원신",
|
69 |
+
"崩坏星穹铁道": "붕괴: 스타레일",
|
70 |
+
"鸣潮": "Wuthering Waves",
|
71 |
+
"选择角色类别": "캐릭터 카테고리 선택",
|
72 |
+
"搜索角色": "캐릭터 검색",
|
73 |
+
"当前选择的角色": "현재 선택된 캐릭터",
|
74 |
+
"选择情绪": "감정 선택",
|
75 |
+
"添加新的声音": "새로운 음성 추가",
|
76 |
+
"### 已选择的角色": "### 선택된 캐릭터",
|
77 |
+
"名称": "이름",
|
78 |
+
"情绪": "감정",
|
79 |
+
"删除": "삭제",
|
80 |
+
"需要合成的文本": "합성할 텍스트",
|
81 |
+
"🎉 合成语音 🎉": "🎉 음성 합성 🎉",
|
82 |
+
"输出的语音": "출력된 음성",
|
83 |
+
"合成时间": "합성 시간",
|
84 |
+
"## 免责声明\n\n本网站提供的语音合成服务(以下简称“服务”)旨在供个人使用和娱乐目的。在使用本服务前,请用户仔细阅读并充分理解以下条款:\n\n1. **角色版权**:本网站可能使用的角色形象涉及第三方知识产权。本网站不拥有这些角色的版权。用户在使用服务时应尊重相关角色的知识产权,并确保其行为不侵犯任何第三方的知识产权。\n\n2. **用户生成内容(UGC)**:用户通过本平台生成的语音内容(以下简称“UGC”)由用户自行负责,与本平台无关。本平台无法控制或审核用户生成的具体内容,且不对UGC的准确性、完整性或合法性承担任何责任。\n\n3. **使用限制**:本服务生成的语音及其UGC仅限于个人使用,不得用于任何商业目的。未经本平台事先书面同意,禁止将生成内容用于任何商业活动。\n\n4. **法律责任**:用户使用本服务所产生的任何法律责任由用户自行承担,与本平台无关。如因用户使用服务或其UGC导致的任何纠纷或损失,本平台不承担任何责任。\n\n5. **版权声明**:用户应尊重原创,不得使用本服务生成侵犯他人著作权的内容。如发现用户生成内容侵犯他人版权,本平台有权立即停止对其提供服务,并保留追究法律责任的权利。\n\n6. **内容监管**:尽管本平台无法控制UGC,但一旦发现违反本免责声明或法律法规的内容,本平台将采取必要措施,包括但不限于删除违规内容,并配合有关部门进行调查。\n\n7. **注明要求**:用户应在生成内容的显著位置,如可能的话,注明“此内容由RubiiTTS生成”或类似的说明。用户应确保注明行为符合本条款的要求。\n\n用户使用本网站即表示同意以上免责声明。如有疑问,请联系我们contact@yomio.ai。\n\n**最终解释权归本网站所有。**": "## 면책 조항\n\n본 웹사이트에서 제공하는 음성 합성 서비스(이하 \"서비스\")는 개인 사용 및 엔터테인먼트 목적으로 제공됩니다. 본 서비스를 사용하기 전에 사용자는 다음 조항을 주의 깊게 읽고 충분히 이해해야 합니다:\n\n1. **캐릭터 저작권**: 본 웹사이트에서 사용되는 캐릭터 이미지는 제3자의 지적 재산권과 관련될 수 있습니다. 본 웹사이트는 이러한 캐릭터들의 저작권을 소유하고 있지 않습니다. 사용자는 서비스를 사용할 때 관련 캐릭터의 지적 재산권을 존중해야 하며, 제3자의 지적 재산권을 침해하지 않도록 해야 합니다.\n\n2. **사용자 생성 콘텐츠(UGC)**: 사용자가 본 플랫폼을 통해 생성한 음성 콘텐츠(이하 \"UGC\")는 사용자 본인의 책임이며, 본 플랫폼과는 무관합니다. 본 플랫폼은 사용자가 생성한 구체적인 내용을 통제하거나 검토할 수 없으며, UGC의 정확성, 완전성 또는 합법성에 대해 어떠한 책임도 지지 않습니다.\n\n3. **사용 제한**: 본 서비스에서 생성된 음성 및 UGC는 개인 사용에 한정되며, 상업적 목적으로 사용할 수 없습니다. 본 플랫폼의 사전 서면 동의 없이 생성된 콘텐츠를 상업적 활동에 사용하는 것은 금지됩니다.\n\n4. **법적 책임**: 사용자가 본 서비스를 사용함으로써 발생하는 모든 법적 책임은 사용자 본인이 부담하며, 본 플랫폼과는 무관합니다. 사용자의 서비스 사용 또는 UGC로 인해 발생하는 모든 분쟁이나 손실에 대해 본 플랫폼은 어떠한 책임도 지지 않습니다.\n\n5. **저작권 선언**: 사용자는 창작물을 존중해야 하며, 본 서비스를 사용하여 타인의 저작권을 침해하는 콘텐츠를 생성해서는 안 됩니다. 사용자가 생성한 콘텐츠가 타인의 저작권을 침해한 것이 발견될 경우, 본 플랫폼은 즉시 서비스 제공을 중단할 권리가 있으며, 법적 책임을 추궁할 권리를 보유합니다.\n\n6. **콘텐츠 관리**: 본 플랫폼은 UGC를 통제할 수 없지만, 본 면책 조항이나 법규를 위반하는 내용이 발견될 경우, 본 플랫폼은 필요한 조치를 취할 것입니다. 이는 위반 콘텐츠 삭제 및 관련 부서와의 조사 협력을 포함하지만 이에 국한되지 않습니다.\n\n7. **표시 요구 사항**: 사용자는 생성된 콘텐츠의 눈에 띄는 위치에 가능한 한 \"이 콘텐츠는 RubiiTTS에 의해 생성되었습니다\" 또는 유사한 설명을 표시해야 합니다. 사용자는 이 표시 행위가 본 조항의 요구 사항을 준수하는지 확인해야 합니다.\n\n사용자가 본 웹사이트를 사용하는 것은 위의 면책 조항에 동의한다는 의미입니다. 문의 사항이 있으시면 contact@yomio.ai로 연락해 주시기 바랍니다.\n\n**최종 해석권은 본 웹사이트에 있습니다.**"
|
85 |
+
}
|
86 |
+
}
|
requirements.txt
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
gradio
|
2 |
+
aiohttp
|
3 |
+
pypinyin
|
4 |
+
gradio-i18n
|