VSPAN commited on
Commit
92f766b
·
verified ·
1 Parent(s): a3ce33f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +207 -34
app.py CHANGED
@@ -4,6 +4,10 @@ import asyncio
4
  import tempfile
5
  import re
6
  import emoji
 
 
 
 
7
  # Функция для очистки текста от нежелательных символов и эмодзи
8
  def clean_text(text):
9
  # Удаление указанных символов
@@ -11,16 +15,18 @@ def clean_text(text):
11
  # Удаление эмодзи
12
  text = emoji.replace_emoji(text, replace='')
13
  return text
 
14
  # Get all available voices
15
  async def get_voices():
16
  voices = await edge_tts.list_voices()
17
  return {f"{v['ShortName']} - {v['Locale']} ({v['Gender']})": v['ShortName'] for v in voices}
 
18
  # Text-to-speech function
19
  async def text_to_speech(text, voice, rate, pitch):
20
  if not text.strip():
21
- return None, gr.Warning("Please enter text to convert.")
22
  if not voice:
23
- return None, gr.Warning("Please select a voice.")
24
 
25
  # Очистка текста
26
  text = clean_text(text)
@@ -34,39 +40,206 @@ async def text_to_speech(text, voice, rate, pitch):
34
  try:
35
  await communicate.save(tmp_path)
36
  except Exception as e:
37
- return None, gr.Warning(f"An error occurred during text-to-speech conversion: {str(e)}")
38
  return tmp_path, None
39
- # Gradio interface function
40
- def tts_interface(text, voice, rate, pitch):
41
- audio, warning = asyncio.run(text_to_speech(text, voice, rate, pitch))
42
- return audio, warning
43
- # Create Gradio application
44
- async def create_demo():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  voices = await get_voices()
 
 
 
 
 
 
 
 
 
46
 
47
- description = """
48
- """
49
-
50
- demo = gr.Interface(
51
- fn=tts_interface,
52
- inputs=[
53
- gr.Textbox(label="Input Text", lines=5),
54
- gr.Dropdown(choices=[""] + list(voices.keys()), label="Select Voice", value=""),
55
- gr.Slider(minimum=-50, maximum=50, value=0, label="Speech Rate Adjustment (%)", step=1),
56
- gr.Slider(minimum=-20, maximum=20, value=0, label="Pitch Adjustment (Hz)", step=1)
57
- ],
58
- outputs=[
59
- gr.Audio(label="Generated Audio", type="filepath"),
60
- gr.Markdown(label="Warning", visible=False)
61
- ],
62
- title="Edge TTS Text-to-Speech",
63
- description=description,
64
- article="",
65
- analytics_enabled=False,
66
- allow_flagging="manual"
67
- )
68
- return demo
69
- # Run the application
70
  if __name__ == "__main__":
71
- demo = asyncio.run(create_demo())
72
- demo.launch()
 
4
  import tempfile
5
  import re
6
  import emoji
7
+ from flask import Flask, request, jsonify, render_template_string
8
+
9
+ app = Flask(__name__)
10
+
11
  # Функция для очистки текста от нежелательных символов и эмодзи
12
  def clean_text(text):
13
  # Удаление указанных символов
 
15
  # Удаление эмодзи
16
  text = emoji.replace_emoji(text, replace='')
17
  return text
18
+
19
  # Get all available voices
20
  async def get_voices():
21
  voices = await edge_tts.list_voices()
22
  return {f"{v['ShortName']} - {v['Locale']} ({v['Gender']})": v['ShortName'] for v in voices}
23
+
24
  # Text-to-speech function
25
  async def text_to_speech(text, voice, rate, pitch):
26
  if not text.strip():
27
+ return None, "Пожалуйста, введите текст для озвучки."
28
  if not voice:
29
+ return None, "Пожалуйста, выберите голос."
30
 
31
  # Очистка текста
32
  text = clean_text(text)
 
40
  try:
41
  await communicate.save(tmp_path)
42
  except Exception as e:
43
+ return None, f"Произошла ошибка при конвертации текста в речь: {str(e)}"
44
  return tmp_path, None
45
+
46
+ # HTML шаблон
47
+ HTML_TEMPLATE = """
48
+ <!DOCTYPE html>
49
+ <html lang="ru">
50
+ <head>
51
+ <meta charset="UTF-8">
52
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
53
+ <title>Озвучка с музыкальной атмосферой</title>
54
+ <style>
55
+ body {
56
+ background-color: white;
57
+ color: #FF6347; /* Томатный оранжевый */
58
+ font-family: Arial, sans-serif;
59
+ margin: 0;
60
+ padding: 0;
61
+ }
62
+
63
+ header {
64
+ background-color: #FF6347;
65
+ color: white;
66
+ padding: 10px 20px;
67
+ text-align: center;
68
+ }
69
+
70
+ .container {
71
+ padding: 20px;
72
+ }
73
+
74
+ .audio-control {
75
+ position: fixed;
76
+ bottom: 20px;
77
+ left: 20px;
78
+ background-color: white;
79
+ border: 1px solid #FF6347;
80
+ padding: 10px;
81
+ border-radius: 5px;
82
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
83
+ }
84
+
85
+ button {
86
+ background-color: #FF6347;
87
+ color: white;
88
+ border: none;
89
+ padding: 10px 20px;
90
+ border-radius: 5px;
91
+ cursor: pointer;
92
+ margin-top: 10px;
93
+ }
94
+
95
+ button:hover {
96
+ background-color: #FF4500; /* Темный томатный оранжевый */
97
+ }
98
+ </style>
99
+ </head>
100
+ <body>
101
+ <header>
102
+ <h1>Озвучка с музыкальной атмосферой</h1>
103
+ </header>
104
+ <div class="container">
105
+ <textarea id="text-input" rows="5" placeholder="Введите текст для озвучки"></textarea>
106
+ <br>
107
+ <select id="voice-select">
108
+ <!-- Опции будут заполнены через JavaScript -->
109
+ </select>
110
+ <br>
111
+ <label for="rate-slider">Скорость озвучки:</label>
112
+ <input type="range" id="rate-slider" min="-50" max="50" value="0" step="1">
113
+ <br>
114
+ <label for="pitch-slider">Тон озвучки:</label>
115
+ <input type="range" id="pitch-slider" min="-20" max="20" value="0" step="1">
116
+ <br>
117
+ <button onclick="generateAudio()">Озвучить</button>
118
+ <br>
119
+ <audio id="audio-player" controls style="display:none;"></audio>
120
+ </div>
121
+ <div class="audio-control">
122
+ <label for="atmosphere-select">Атмосфера:</label>
123
+ <select id="atmosphere-select">
124
+ <option value="dynamic">Динамичная</option>
125
+ <option value="calm">Спокойная</option>
126
+ </select>
127
+ <br>
128
+ <button onclick="toggleMusic()">Выключить музыку</button>
129
+ <audio id="background-music" loop></audio>
130
+ </div>
131
+ <script>
132
+ let voices = [];
133
+ let currentMusic = null;
134
+
135
+ document.addEventListener('DOMContentLoaded', () => {
136
+ fetch('/get_voices')
137
+ .then(response => response.json())
138
+ .then(data => {
139
+ voices = data;
140
+ const voiceSelect = document.getElementById('voice-select');
141
+ Object.keys(voices).forEach(key => {
142
+ const option = document.createElement('option');
143
+ option.value = voices[key];
144
+ option.textContent = key;
145
+ voiceSelect.appendChild(option);
146
+ });
147
+ });
148
+
149
+ const atmosphereSelect = document.getElementById('atmosphere-select');
150
+ atmosphereSelect.addEventListener('change', () => {
151
+ changeMusic(atmosphereSelect.value);
152
+ });
153
+
154
+ changeMusic(atmosphereSelect.value); // Устанавливаем музыку по умолчанию
155
+ });
156
+
157
+ function generateAudio() {
158
+ const text = document.getElementById('text-input').value;
159
+ const voice = document.getElementById('voice-select').value;
160
+ const rate = document.getElementById('rate-slider').value;
161
+ const pitch = document.getElementById('pitch-slider').value;
162
+
163
+ if (!text.trim()) {
164
+ alert('Пожалуйста, введите текст для озвучки.');
165
+ return;
166
+ }
167
+ if (!voice) {
168
+ alert('Пожалуйста, выберите голос.');
169
+ return;
170
+ }
171
+
172
+ fetch('/tts', {
173
+ method: 'POST',
174
+ headers: {
175
+ 'Content-Type': 'application/json'
176
+ },
177
+ body: JSON.stringify({ text, voice, rate, pitch })
178
+ })
179
+ .then(response => response.json())
180
+ .then(data => {
181
+ if (data.warning) {
182
+ alert(data.warning);
183
+ } else {
184
+ const audioPlayer = document.getElementById('audio-player');
185
+ audioPlayer.src = data.audio;
186
+ audioPlayer.style.display = 'block';
187
+ audioPlayer.play();
188
+ audioPlayer.addEventListener('ended', () => {
189
+ document.getElementById('background-music').play();
190
+ });
191
+ }
192
+ });
193
+ }
194
+
195
+ function changeMusic(atmosphere) {
196
+ const backgroundMusic = document.getElementById('background-music');
197
+ backgroundMusic.src = `/music/${atmosphere}.mp3`;
198
+ backgroundMusic.play();
199
+ currentMusic = atmosphere;
200
+ }
201
+
202
+ function toggleMusic() {
203
+ const backgroundMusic = document.getElementById('background-music');
204
+ if (backgroundMusic.paused) {
205
+ backgroundMusic.play();
206
+ document.querySelector('.audio-control button').textContent = 'Выключить музыку';
207
+ } else {
208
+ backgroundMusic.pause();
209
+ document.querySelector('.audio-control button').textContent = 'Включить музыку';
210
+ }
211
+ }
212
+ </script>
213
+ </body>
214
+ </html>
215
+ """
216
+
217
+ @app.route('/get_voices', methods=['GET'])
218
+ async def get_voices_route():
219
  voices = await get_voices()
220
+ return jsonify(voices)
221
+
222
+ @app.route('/tts', methods=['POST'])
223
+ async def tts_route():
224
+ data = request.get_json()
225
+ text = data.get('text', '')
226
+ voice = data.get('voice', '')
227
+ rate = data.get('rate', 0)
228
+ pitch = data.get('pitch', 0)
229
 
230
+ audio, warning = await text_to_speech(text, voice, rate, pitch)
231
+ if warning:
232
+ return jsonify({'warning': warning})
233
+ else:
234
+ return jsonify({'audio': audio})
235
+
236
+ @app.route('/')
237
+ def index():
238
+ return render_template_string(HTML_TEMPLATE)
239
+
240
+ @app.route('/music/<path:path>')
241
+ def serve_music(path):
242
+ return app.send_static_file(f'music/{path}')
243
+
 
 
 
 
 
 
 
 
 
244
  if __name__ == "__main__":
245
+ app.run(debug=True)