VSPAN commited on
Commit
f9f89f2
·
verified ·
1 Parent(s): ef6f699

Update style.css

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