Update index.html
Browse files- index.html +660 -646
index.html
CHANGED
@@ -5,708 +5,441 @@
|
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
<title>Alpha TTS Nova - نسل جدید تبدیل متن به صدا</title>
|
7 |
<style>
|
8 |
-
@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;600;700;800&display=swap');
|
9 |
|
10 |
:root {
|
11 |
-
--
|
12 |
-
--
|
13 |
-
--
|
14 |
-
--
|
15 |
-
--
|
16 |
-
--
|
17 |
-
--
|
18 |
-
--
|
19 |
-
--
|
20 |
-
--
|
21 |
-
--
|
22 |
-
--
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
|
|
29 |
}
|
30 |
|
31 |
body {
|
32 |
-
font-family:
|
33 |
direction: rtl;
|
34 |
-
background:
|
35 |
-
background-size: 400% 400%;
|
36 |
-
animation: gradientShift 15s ease infinite;
|
37 |
color: var(--text-primary);
|
|
|
|
|
|
|
|
|
38 |
min-height: 100vh;
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
content: '';
|
45 |
-
position: fixed;
|
46 |
-
top: 0;
|
47 |
-
left: 0;
|
48 |
-
width: 100%;
|
49 |
-
height: 100%;
|
50 |
-
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grain" width="100" height="100" patternUnits="userSpaceOnUse"><circle cx="25" cy="25" r="1" fill="rgba(255,255,255,0.1)"/><circle cx="75" cy="75" r="1" fill="rgba(255,255,255,0.05)"/><circle cx="50" cy="10" r="0.5" fill="rgba(255,255,255,0.08)"/></pattern></defs><rect width="100" height="100" fill="url(%23grain)"/></svg>');
|
51 |
-
pointer-events: none;
|
52 |
-
z-index: -1;
|
53 |
-
}
|
54 |
-
|
55 |
-
@keyframes gradientShift {
|
56 |
-
0% { background-position: 0% 50%; }
|
57 |
-
50% { background-position: 100% 50%; }
|
58 |
-
100% { background-position: 0% 50%; }
|
59 |
-
}
|
60 |
-
|
61 |
-
.floating-shapes {
|
62 |
-
position: fixed;
|
63 |
-
top: 0;
|
64 |
-
left: 0;
|
65 |
-
width: 100%;
|
66 |
-
height: 100%;
|
67 |
-
pointer-events: none;
|
68 |
-
z-index: -1;
|
69 |
-
}
|
70 |
-
|
71 |
-
.shape {
|
72 |
-
position: absolute;
|
73 |
-
background: rgba(255, 255, 255, 0.1);
|
74 |
-
border-radius: 50%;
|
75 |
-
animation: float 20s infinite linear;
|
76 |
-
}
|
77 |
-
|
78 |
-
.shape:nth-child(1) {
|
79 |
-
width: 80px;
|
80 |
-
height: 80px;
|
81 |
-
top: 20%;
|
82 |
-
left: 10%;
|
83 |
-
animation-delay: 0s;
|
84 |
-
}
|
85 |
-
|
86 |
-
.shape:nth-child(2) {
|
87 |
-
width: 120px;
|
88 |
-
height: 120px;
|
89 |
-
top: 60%;
|
90 |
-
left: 80%;
|
91 |
-
animation-delay: -5s;
|
92 |
-
}
|
93 |
-
|
94 |
-
.shape:nth-child(3) {
|
95 |
-
width: 60px;
|
96 |
-
height: 60px;
|
97 |
-
top: 80%;
|
98 |
-
left: 20%;
|
99 |
-
animation-delay: -10s;
|
100 |
-
}
|
101 |
-
|
102 |
-
@keyframes float {
|
103 |
-
0% { transform: translateY(0px) rotate(0deg); opacity: 0.7; }
|
104 |
-
33% { transform: translateY(-20px) rotate(120deg); opacity: 0.4; }
|
105 |
-
66% { transform: translateY(10px) rotate(240deg); opacity: 0.7; }
|
106 |
-
100% { transform: translateY(0px) rotate(360deg); opacity: 0.4; }
|
107 |
}
|
108 |
|
109 |
-
.container {
|
110 |
-
max-width:
|
|
|
111 |
margin: 0 auto;
|
112 |
-
padding: 2rem;
|
113 |
-
position: relative;
|
114 |
-
z-index: 1;
|
115 |
}
|
116 |
|
117 |
-
.app-header {
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
}
|
122 |
-
|
123 |
-
|
124 |
-
font-
|
125 |
-
|
126 |
-
background: linear-gradient(
|
127 |
-webkit-background-clip: text;
|
128 |
-webkit-text-fill-color: transparent;
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
}
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
.form-group:nth-child(3) { animation-delay: 0.3s; }
|
188 |
-
.form-group:nth-child(4) { animation-delay: 0.4s; }
|
189 |
-
|
190 |
-
label {
|
191 |
-
display: block;
|
192 |
-
font-weight: 600;
|
193 |
-
color: var(--text-light);
|
194 |
-
font-size: 1.1rem;
|
195 |
-
margin-bottom: 1rem;
|
196 |
-
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
197 |
-
}
|
198 |
-
|
199 |
-
textarea, input[type="text"] {
|
200 |
-
width: 100%;
|
201 |
-
padding: 1.2rem 1.5rem;
|
202 |
-
border: 2px solid rgba(255, 255, 255, 0.2);
|
203 |
-
border-radius: 15px;
|
204 |
-
background: rgba(255, 255, 255, 0.1);
|
205 |
-
backdrop-filter: blur(10px);
|
206 |
-
color: var(--text-light);
|
207 |
-
font-family: 'Vazirmatn', sans-serif;
|
208 |
-
font-size: 1rem;
|
209 |
-
transition: var(--transition);
|
210 |
-
resize: vertical;
|
211 |
-
}
|
212 |
-
|
213 |
-
textarea::placeholder, input[type="text"]::placeholder {
|
214 |
-
color: rgba(255, 255, 255, 0.6);
|
215 |
-
}
|
216 |
-
|
217 |
-
textarea:focus, input[type="text"]:focus {
|
218 |
-
outline: none;
|
219 |
-
border-color: rgba(255, 255, 255, 0.6);
|
220 |
-
background: rgba(255, 255, 255, 0.15);
|
221 |
-
box-shadow: 0 0 25px rgba(255, 255, 255, 0.2);
|
222 |
-
transform: translateY(-2px);
|
223 |
-
}
|
224 |
-
|
225 |
-
textarea {
|
226 |
-
min-height: 120px;
|
227 |
-
}
|
228 |
-
|
229 |
-
/* Speaker Selection Styles */
|
230 |
-
#selected-speaker-display {
|
231 |
-
text-align: center;
|
232 |
-
margin-top: 1rem;
|
233 |
-
}
|
234 |
-
|
235 |
-
#selected-speaker-card {
|
236 |
-
display: inline-flex;
|
237 |
-
align-items: center;
|
238 |
-
background: rgba(255, 255, 255, 0.1);
|
239 |
-
backdrop-filter: blur(15px);
|
240 |
-
border: 2px solid rgba(255, 255, 255, 0.2);
|
241 |
-
border-radius: 20px;
|
242 |
-
padding: 1rem 1.5rem;
|
243 |
-
transition: var(--transition);
|
244 |
cursor: pointer;
|
245 |
-
position: relative;
|
246 |
-
overflow: hidden;
|
247 |
-
}
|
248 |
-
|
249 |
-
#selected-speaker-card::before {
|
250 |
-
content: '';
|
251 |
-
position: absolute;
|
252 |
-
top: 0;
|
253 |
-
left: -100%;
|
254 |
-
width: 100%;
|
255 |
-
height: 100%;
|
256 |
-
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
|
257 |
-
transition: left 0.6s;
|
258 |
-
}
|
259 |
-
|
260 |
-
#selected-speaker-card:hover::before {
|
261 |
-
left: 100%;
|
262 |
}
|
263 |
-
|
264 |
#selected-speaker-card:hover {
|
265 |
-
transform: translateY(-
|
266 |
-
box-shadow:
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
border
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
box-shadow: 0 0 20px rgba(255, 255, 255, 0.3);
|
278 |
-
transition: var(--transition);
|
279 |
}
|
280 |
-
|
281 |
#selected-speaker-card:hover img {
|
282 |
-
transform: scale(1.
|
283 |
-
|
284 |
-
}
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
}
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
}
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
#change-speaker-btn:hover::before {
|
327 |
-
width: 100%;
|
328 |
-
}
|
329 |
-
|
330 |
-
#change-speaker-btn:hover {
|
331 |
-
transform: translateY(-3px);
|
332 |
-
box-shadow: 0 10px 25px rgba(255, 255, 255, 0.2);
|
333 |
-
border-color: rgba(255, 255, 255, 0.8);
|
334 |
-
}
|
335 |
-
|
336 |
-
/* Modal Styles */
|
337 |
-
#speaker-modal {
|
338 |
-
position: fixed;
|
339 |
-
top: 0;
|
340 |
-
left: 0;
|
341 |
-
width: 100%;
|
342 |
-
height: 100%;
|
343 |
-
background: rgba(0, 0, 0, 0.8);
|
344 |
-
backdrop-filter: blur(10px);
|
345 |
-
display: none;
|
346 |
-
align-items: center;
|
347 |
-
justify-content: center;
|
348 |
-
z-index: 1000;
|
349 |
opacity: 0;
|
350 |
-
transition: opacity 0.4s ease;
|
351 |
-
}
|
352 |
-
|
353 |
-
#speaker-modal.visible {
|
354 |
-
display: flex;
|
355 |
-
opacity: 1;
|
356 |
-
}
|
357 |
-
|
358 |
-
.modal-content {
|
359 |
-
background: rgba(255, 255, 255, 0.15);
|
360 |
-
backdrop-filter: blur(25px);
|
361 |
-
border: 1px solid rgba(255, 255, 255, 0.2);
|
362 |
-
border-radius: 25px;
|
363 |
-
padding: 2.5rem;
|
364 |
-
width: 90%;
|
365 |
-
max-width: 800px;
|
366 |
-
max-height: 85vh;
|
367 |
-
overflow-y: auto;
|
368 |
-
transform: scale(0.9) translateY(50px);
|
369 |
-
transition: transform 0.4s ease, opacity 0.4s ease;
|
370 |
-
opacity: 0;
|
371 |
-
}
|
372 |
-
|
373 |
-
#speaker-modal.visible .modal-content {
|
374 |
-
transform: scale(1) translateY(0);
|
375 |
-
opacity: 1;
|
376 |
-
}
|
377 |
-
|
378 |
-
.modal-header {
|
379 |
-
display: flex;
|
380 |
-
justify-content: space-between;
|
381 |
-
align-items: center;
|
382 |
-
margin-bottom: 2rem;
|
383 |
-
padding-bottom: 1rem;
|
384 |
-
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
385 |
-
}
|
386 |
-
|
387 |
-
.modal-header h2 {
|
388 |
-
margin: 0;
|
389 |
-
font-size: 1.8rem;
|
390 |
-
color: var(--text-light);
|
391 |
-
font-weight: 700;
|
392 |
-
}
|
393 |
-
|
394 |
-
.close-modal-btn {
|
395 |
-
background: none;
|
396 |
-
border: none;
|
397 |
-
font-size: 2rem;
|
398 |
-
cursor: pointer;
|
399 |
-
color: rgba(255, 255, 255, 0.8);
|
400 |
-
transition: var(--transition);
|
401 |
-
width: 40px;
|
402 |
-
height: 40px;
|
403 |
-
border-radius: 50%;
|
404 |
-
display: flex;
|
405 |
-
align-items: center;
|
406 |
-
justify-content: center;
|
407 |
}
|
408 |
-
|
409 |
-
.
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
#speaker-grid {
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
422 |
cursor: pointer;
|
423 |
-
transition:
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
.speaker-card:hover img {
|
457 |
-
transform: scale(1.1);
|
458 |
-
}
|
459 |
-
|
460 |
-
.speaker-card .speaker-name {
|
461 |
-
padding: 1rem;
|
462 |
-
font-weight: 600;
|
463 |
-
font-size: 0.95rem;
|
464 |
-
color: var(--text-light);
|
465 |
-
}
|
466 |
-
|
467 |
-
.speaker-card input[type="radio"]:checked + .speaker-visual {
|
468 |
-
border-color: #4facfe;
|
469 |
-
box-shadow: 0 0 25px rgba(79, 172, 254, 0.5);
|
470 |
-
background: rgba(79, 172, 254, 0.2);
|
471 |
-
}
|
472 |
-
|
473 |
-
/* Slider Styles */
|
474 |
-
.slider-container {
|
475 |
-
display: flex;
|
476 |
-
align-items: center;
|
477 |
-
gap: 1.5rem;
|
478 |
-
}
|
479 |
-
|
480 |
-
input[type="range"] {
|
481 |
-
flex-grow: 1;
|
482 |
-
-webkit-appearance: none;
|
483 |
-
width: 100%;
|
484 |
-
height: 8px;
|
485 |
-
background: rgba(255, 255, 255, 0.2);
|
486 |
-
border-radius: 10px;
|
487 |
-
outline: none;
|
488 |
-
cursor: pointer;
|
489 |
-
transition: background 0.3s;
|
490 |
-
}
|
491 |
-
|
492 |
-
input[type="range"]::-webkit-slider-track {
|
493 |
-
background: linear-gradient(90deg, #4facfe 0%, #00f2fe 100%);
|
494 |
-
height: 8px;
|
495 |
-
border-radius: 10px;
|
496 |
-
}
|
497 |
-
|
498 |
-
input[type="range"]::-webkit-slider-thumb {
|
499 |
-
-webkit-appearance: none;
|
500 |
-
width: 24px;
|
501 |
-
height: 24px;
|
502 |
-
background: #fff;
|
503 |
-
border-radius: 50%;
|
504 |
-
cursor: pointer;
|
505 |
-
box-shadow: 0 0 15px rgba(79, 172, 254, 0.5);
|
506 |
-
transition: transform 0.2s ease;
|
507 |
-
}
|
508 |
-
|
509 |
-
input[type="range"]::-webkit-slider-thumb:hover {
|
510 |
-
transform: scale(1.2);
|
511 |
-
box-shadow: 0 0 25px rgba(79, 172, 254, 0.8);
|
512 |
-
}
|
513 |
-
|
514 |
-
#temperature-value {
|
515 |
-
font-weight: 600;
|
516 |
-
background: rgba(255, 255, 255, 0.2);
|
517 |
-
color: var(--text-light);
|
518 |
-
padding: 0.5rem 1rem;
|
519 |
-
border-radius: 15px;
|
520 |
-
border: 1px solid rgba(255, 255, 255, 0.3);
|
521 |
-
min-width: 60px;
|
522 |
-
text-align: center;
|
523 |
-
backdrop-filter: blur(10px);
|
524 |
-
}
|
525 |
-
|
526 |
-
/* Generate Button */
|
527 |
-
#generate-btn {
|
528 |
-
width: 100%;
|
529 |
-
padding: 1.5rem 2rem;
|
530 |
-
font-size: 1.3rem;
|
531 |
-
font-weight: 700;
|
532 |
-
font-family: 'Vazirmatn', sans-serif;
|
533 |
-
background: var(--accent-gradient);
|
534 |
-
color: var(--text-light);
|
535 |
border: none;
|
536 |
-
|
537 |
-
|
538 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
539 |
position: relative;
|
540 |
overflow: hidden;
|
541 |
-
|
542 |
-
margin-top: 1rem;
|
543 |
}
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
width: 100%;
|
551 |
-
height: 100%;
|
552 |
-
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent);
|
553 |
-
transition: left 0.8s;
|
554 |
}
|
555 |
-
|
556 |
-
#generate-btn:hover
|
557 |
-
|
|
|
558 |
}
|
559 |
-
|
560 |
-
|
561 |
-
transform: translateY(-5px);
|
562 |
-
box-shadow: 0 20px 40px rgba(79, 172, 254, 0.6);
|
563 |
}
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
cursor: not-allowed;
|
568 |
transform: none;
|
569 |
-
box-shadow: none;
|
570 |
}
|
571 |
-
|
572 |
-
|
573 |
-
#output-section {
|
574 |
-
margin-top:
|
575 |
-
padding:
|
576 |
-
background:
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
border: 2px dashed rgba(255, 255, 255, 0.3);
|
586 |
-
transition: var(--transition);
|
587 |
-
animation: slideInUp 1s ease-out 0.6s both;
|
588 |
}
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
font-size: 1.2rem;
|
595 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
596 |
|
597 |
-
#audio-player {
|
598 |
-
width: 100%;
|
599 |
-
margin-top: 1rem;
|
600 |
-
display: none;
|
601 |
-
border-radius: 15px;
|
602 |
-
background: rgba(255, 255, 255, 0.1);
|
603 |
-
}
|
604 |
|
605 |
-
/*
|
606 |
#loading-animation-wrapper {
|
607 |
-
display: none;
|
608 |
flex-direction: column;
|
609 |
align-items: center;
|
610 |
justify-content: center;
|
611 |
gap: 2rem;
|
612 |
width: 100%;
|
613 |
-
min-height:
|
614 |
}
|
615 |
|
616 |
-
.
|
617 |
-
width: 120px;
|
618 |
height: 120px;
|
619 |
position: relative;
|
620 |
-
animation: rotate-
|
621 |
}
|
622 |
-
|
623 |
-
.
|
624 |
position: absolute;
|
625 |
-
|
626 |
-
|
627 |
border-radius: 50%;
|
628 |
-
|
629 |
-
box-shadow: 0 0 15px #4facfe, 0 0 30px #00f2fe;
|
630 |
-
animation: node-pulse 2s ease-in-out infinite;
|
631 |
}
|
632 |
|
633 |
-
.
|
634 |
-
.
|
635 |
-
.
|
636 |
-
.neural-node:nth-child(4) { bottom: 15%; right: 15%; animation-delay: -0.75s; background: #00f2fe; }
|
637 |
-
.neural-node:nth-child(5) { bottom: 0; left: 50%; transform: translateX(-50%); animation-delay: -1s; }
|
638 |
-
.neural-node:nth-child(6) { bottom: 15%; left: 15%; animation-delay: -1.25s; background: #00f2fe; }
|
639 |
-
.neural-node:nth-child(7) { top: 50%; left: 0; transform: translateY(-50%); animation-delay: -1.5s; }
|
640 |
-
.neural-node:nth-child(8) { top: 15%; left: 15%; animation-delay: -1.75s; background: #00f2fe; }
|
641 |
|
642 |
-
.
|
643 |
position: absolute;
|
644 |
-
height:
|
645 |
-
|
646 |
-
|
647 |
-
|
|
|
648 |
}
|
649 |
|
650 |
-
|
|
|
|
|
|
|
|
|
|
|
651 |
from { transform: rotate(0deg); }
|
652 |
to { transform: rotate(360deg); }
|
653 |
}
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
50% { transform: scale(1.4); opacity: 1; }
|
658 |
}
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
50% { opacity: 0.8; }
|
663 |
}
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
text-align: center;
|
670 |
-
animation: text-glow 2s ease-in-out infinite alternate;
|
671 |
}
|
672 |
-
|
673 |
-
@keyframes
|
674 |
-
from {
|
675 |
-
to {
|
|
|
|
|
|
|
|
|
|
|
676 |
}
|
677 |
|
678 |
-
/* Responsive Design */
|
679 |
-
@media (max-width: 768px) {
|
680 |
-
.container {
|
681 |
-
padding: 1rem;
|
682 |
-
}
|
683 |
-
|
684 |
-
.app-header h1 {
|
685 |
-
font-size: 2.5rem;
|
686 |
-
}
|
687 |
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
}
|
|
|
697 |
</style>
|
698 |
</head>
|
699 |
<body>
|
700 |
-
<div class="floating-shapes">
|
701 |
-
<div class="shape"></div>
|
702 |
-
<div class="shape"></div>
|
703 |
-
<div class="shape"></div>
|
704 |
-
</div>
|
705 |
-
|
706 |
<div class="container">
|
707 |
<header class="app-header">
|
708 |
<h1>آلفا TTS Nova</h1>
|
709 |
-
<p>صدایی نو، تجربهای نوین در تبدیل متن به
|
710 |
</header>
|
711 |
|
712 |
<main class="main-content">
|
@@ -715,9 +448,8 @@
|
|
715 |
<label for="text-input">📝 متن مورد نظر شما</label>
|
716 |
<textarea id="text-input" rows="4" placeholder="متن خود را اینجا وارد کنید...">سلام دنیا! این یک نمونه متن برای آزمایش نسل جدید تبدیل متن به صدا آلفا نوا است. امیدوارم از کیفیت آن لذت ببرید.</textarea>
|
717 |
</div>
|
718 |
-
|
719 |
<div class="form-group">
|
720 |
-
<label for="prompt-input">🗣️ سبک و لحن گفتار (اختیاری)</label>
|
721 |
<input type="text" id="prompt-input" value="با صدایی شفاف، دوستانه و پرانرژی." placeholder="مثال: با لحنی رسمی و موقر، یا شاد و کودکانه">
|
722 |
</div>
|
723 |
|
@@ -728,10 +460,10 @@
|
|
728 |
<img id="selected-speaker-img" src="" alt="عکس گوینده">
|
729 |
<div id="selected-speaker-info">
|
730 |
<h3 id="selected-speaker-name"></h3>
|
731 |
-
<p>گوینده پیشفرض</p>
|
732 |
</div>
|
733 |
</div>
|
734 |
-
|
735 |
</div>
|
736 |
</div>
|
737 |
|
@@ -749,15 +481,16 @@
|
|
749 |
<div id="output-section">
|
750 |
<div id="status-message">صدای تولید شده در اینجا نمایش داده خواهد شد.</div>
|
751 |
<div id="loading-animation-wrapper">
|
752 |
-
|
753 |
-
<div class="
|
754 |
-
|
755 |
-
|
756 |
-
<div class="
|
757 |
-
|
758 |
-
|
759 |
-
<div class="
|
760 |
-
|
|
|
761 |
</div>
|
762 |
<p id="loading-text">در حال پردازش هوشمند و تولید صدا...</p>
|
763 |
</div>
|
@@ -770,7 +503,7 @@
|
|
770 |
<div class="modal-content">
|
771 |
<div class="modal-header">
|
772 |
<h2>گالری گویندگان آلفا نوا</h2>
|
773 |
-
<button type="button" class="close-modal-btn">×</button>
|
774 |
</div>
|
775 |
<div id="speaker-grid"></div>
|
776 |
</div>
|
@@ -780,5 +513,286 @@
|
|
780 |
|
781 |
<script>
|
782 |
document.addEventListener('DOMContentLoaded', () => {
|
783 |
-
const HF_SPACE_URL = "https://hamed744-ttspro.hf.space";
|
784 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
<title>Alpha TTS Nova - نسل جدید تبدیل متن به صدا</title>
|
7 |
<style>
|
8 |
+
@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;600;700;800;900&display=swap');
|
9 |
|
10 |
:root {
|
11 |
+
--app-font: 'Vazirmatn', sans-serif;
|
12 |
+
--app-bg: #F4F7FC; /* Slightly lighter and cooler */
|
13 |
+
--panel-bg: #FFFFFF;
|
14 |
+
--panel-border: #E8EEF3; /* Softer border */
|
15 |
+
--text-primary: #121826; /* Darker, richer black */
|
16 |
+
--text-secondary: #5C677D; /* Softer secondary text */
|
17 |
+
--accent-primary: #3B82F6; /* Brighter Blue */
|
18 |
+
--accent-primary-hover: #2563EB; /* Darker Blue for hover */
|
19 |
+
--accent-secondary: #10B981; /* Vibrant Teal/Green */
|
20 |
+
--accent-secondary-hover: #059669;
|
21 |
+
--input-bg: #F8FAFC; /* Slightly off-white for inputs */
|
22 |
+
--input-border-focus: var(--accent-primary);
|
23 |
+
|
24 |
+
--radius-card: 24px; /* More rounded */
|
25 |
+
--radius-input: 14px; /* More rounded */
|
26 |
+
--shadow-subtle: 0 2px 8px rgba(26, 32, 44, 0.04);
|
27 |
+
--shadow-medium: 0 6px 16px -2px rgba(26, 32, 44, 0.08), 0 3px 8px -3px rgba(26,32,44,0.05);
|
28 |
+
--shadow-strong: 0 10px 25px -5px rgba(26, 32, 44, 0.12), 0 6px 15px -6px rgba(26,32,44,0.08);
|
29 |
+
--transition-smooth: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); /* Smooth transition */
|
30 |
}
|
31 |
|
32 |
body {
|
33 |
+
font-family: var(--app-font);
|
34 |
direction: rtl;
|
35 |
+
background-color: var(--app-bg);
|
|
|
|
|
36 |
color: var(--text-primary);
|
37 |
+
font-size: 16px;
|
38 |
+
line-height: 1.8; /* Increased line-height for better readability */
|
39 |
+
margin: 0;
|
40 |
+
padding: 2.5rem 0; /* More padding */
|
41 |
min-height: 100vh;
|
42 |
+
-webkit-font-smoothing: antialiased;
|
43 |
+
-moz-osx-font-smoothing: grayscale;
|
44 |
+
display: flex;
|
45 |
+
justify-content: center;
|
46 |
+
align-items: flex-start;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
}
|
48 |
|
49 |
+
.container {
|
50 |
+
max-width: 780px; /* Slightly wider */
|
51 |
+
width: 92%;
|
52 |
margin: 0 auto;
|
|
|
|
|
|
|
53 |
}
|
54 |
|
55 |
+
.app-header {
|
56 |
+
padding: 0.5rem 0 3rem 0;
|
57 |
+
text-align: center;
|
58 |
+
margin-bottom: 2rem;
|
59 |
}
|
60 |
+
.app-header h1 {
|
61 |
+
font-size: 3.2em; /* Larger */
|
62 |
+
font-weight: 900; /* Bolder */
|
63 |
+
margin:0 0 0.75rem 0;
|
64 |
+
background: linear-gradient(45deg, var(--accent-primary), var(--accent-secondary));
|
65 |
-webkit-background-clip: text;
|
66 |
-webkit-text-fill-color: transparent;
|
67 |
+
letter-spacing: -1px;
|
68 |
+
}
|
69 |
+
.app-header p {
|
70 |
+
font-size: 1.25em; /* Larger */
|
71 |
+
color: var(--text-secondary);
|
72 |
+
margin-top:0;
|
73 |
+
opacity: 1;
|
74 |
+
font-weight: 400;
|
75 |
+
}
|
76 |
+
|
77 |
+
.main-content {
|
78 |
+
padding: 3rem; /* More padding */
|
79 |
+
background-color: var(--panel-bg);
|
80 |
+
border-radius: var(--radius-card);
|
81 |
+
box-shadow: var(--shadow-strong); /* Stronger shadow for main panel */
|
82 |
+
border: 1px solid var(--panel-border);
|
83 |
+
}
|
84 |
+
|
85 |
+
.form-group { margin-bottom: 2.5rem; }
|
86 |
+
label {
|
87 |
+
display: block;
|
88 |
+
font-weight: 700; /* Bolder labels */
|
89 |
+
color: var(--text-primary);
|
90 |
+
font-size: 1.1em;
|
91 |
+
margin-bottom: 1rem;
|
92 |
+
}
|
93 |
+
textarea, input[type="text"] {
|
94 |
+
width: 100%;
|
95 |
+
padding: 1rem 1.2rem; /* More padding */
|
96 |
+
border-radius: var(--radius-input);
|
97 |
+
border: 1px solid var(--panel-border);
|
98 |
+
background-color: var(--input-bg);
|
99 |
+
color: var(--text-primary);
|
100 |
+
box-shadow: var(--shadow-subtle) inset; /* Subtle inset shadow */
|
101 |
+
font-family: var(--app-font);
|
102 |
+
font-size: 1.05rem;
|
103 |
+
box-sizing: border-box;
|
104 |
+
transition: var(--transition-smooth);
|
105 |
+
}
|
106 |
+
textarea:focus, input[type="text"]:focus {
|
107 |
+
outline: none;
|
108 |
+
border-color: var(--input-border-focus);
|
109 |
+
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.2), var(--shadow-subtle) inset; /* Enhanced focus */
|
110 |
+
background-color: #fff;
|
111 |
+
}
|
112 |
+
textarea { min-height: 120px; resize: vertical; }
|
113 |
+
|
114 |
+
/* --- نمایش گوینده منتخب --- */
|
115 |
+
#selected-speaker-display { text-align: center; margin-top: 1rem; position: relative; }
|
116 |
+
#selected-speaker-card {
|
117 |
+
display: inline-flex;
|
118 |
+
align-items: center;
|
119 |
+
background: linear-gradient(135deg, var(--input-bg) 0%, #fff 100%);
|
120 |
+
border-radius: var(--radius-card); /* Match card radius */
|
121 |
+
padding: 1rem 1.2rem; /* R L T B -- More padding */
|
122 |
+
box-shadow: var(--shadow-medium);
|
123 |
+
border: 1px solid var(--panel-border);
|
124 |
+
transition: var(--transition-smooth);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
125 |
cursor: pointer;
|
126 |
+
position: relative; /* For pseudo-elements if needed */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
127 |
}
|
|
|
128 |
#selected-speaker-card:hover {
|
129 |
+
transform: translateY(-4px) scale(1.03);
|
130 |
+
box-shadow: var(--shadow-strong);
|
131 |
+
}
|
132 |
+
#selected-speaker-card img {
|
133 |
+
width: 80px; height: 80px; /* Larger image */
|
134 |
+
border-radius: 50%;
|
135 |
+
object-fit: cover;
|
136 |
+
margin-left: 20px;
|
137 |
+
border: 4px solid var(--accent-secondary);
|
138 |
+
box-shadow: 0 0 15px -3px rgba(16, 185, 129, 0.6);
|
139 |
+
background-color: #e0e0e0;
|
140 |
+
transition: var(--transition-smooth);
|
|
|
|
|
141 |
}
|
|
|
142 |
#selected-speaker-card:hover img {
|
143 |
+
transform: scale(1.05) rotate(3deg);
|
144 |
+
}
|
145 |
+
#selected-speaker-info h3 { margin: 0; font-size: 1.45em; font-weight: 800; color: var(--text-primary); }
|
146 |
+
#selected-speaker-info p { margin: 5px 0 0; color: var(--text-secondary); font-size: 0.9em; font-weight: 500; }
|
147 |
+
|
148 |
+
#change-speaker-btn { /* Style updated, but will be primarily triggered by card click */
|
149 |
+
display: block; margin: 1.5rem auto 0;
|
150 |
+
padding: 12px 28px;
|
151 |
+
border-radius: var(--radius-input);
|
152 |
+
background: transparent;
|
153 |
+
border: 2px solid var(--accent-primary);
|
154 |
+
color: var(--accent-primary);
|
155 |
+
cursor: pointer;
|
156 |
+
font-family: var(--app-font); font-weight: 700;
|
157 |
+
transition: var(--transition-smooth);
|
158 |
+
}
|
159 |
+
#change-speaker-btn:hover {
|
160 |
+
background-color: var(--accent-primary);
|
161 |
+
color: #fff;
|
162 |
+
transform: translateY(-3px) scale(1.02);
|
163 |
+
box-shadow: 0 6px 12px -3px rgba(59, 130, 246, 0.4);
|
164 |
+
}
|
165 |
+
|
166 |
+
/* --- مودال گالری گویندگان --- */
|
167 |
+
#speaker-modal {
|
168 |
+
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
169 |
+
background-color: rgba(18, 24, 38, 0.6); /* Darker backdrop */
|
170 |
+
backdrop-filter: blur(10px) saturate(180%);
|
171 |
+
display: none; align-items: center; justify-content: center;
|
172 |
+
z-index: 1000; opacity: 0;
|
173 |
+
transition: opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
174 |
+
}
|
175 |
+
#speaker-modal.visible { display: flex; opacity: 1; }
|
176 |
+
.modal-content {
|
177 |
+
background: var(--panel-bg);
|
178 |
+
padding: 2.5rem;
|
179 |
+
border-radius: var(--radius-card);
|
180 |
+
width: 90%; max-width: 720px; /* Wider modal */
|
181 |
+
max-height: 90vh; overflow-y: auto;
|
182 |
+
transform: scale(0.9) translateY(30px);
|
183 |
+
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.4s;
|
184 |
+
border: 1px solid var(--panel-border);
|
185 |
+
box-shadow: var(--shadow-strong);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
186 |
opacity: 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
187 |
}
|
188 |
+
#speaker-modal.visible .modal-content { transform: scale(1) translateY(0); opacity: 1;}
|
189 |
+
.modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 2rem; padding-bottom: 1.2rem; border-bottom: 1px solid var(--panel-border); }
|
190 |
+
.modal-header h2 { margin: 0; font-size: 1.8em; font-weight: 800; color: var(--accent-primary);}
|
191 |
+
.close-modal-btn { background: none; border: none; font-size: 2.8rem; cursor: pointer; color: var(--text-secondary); transition: var(--transition-smooth); line-height: 1; }
|
192 |
+
.close-modal-btn:hover { color: var(--accent-primary); transform: rotate(180deg) scale(1.1); }
|
193 |
+
|
194 |
+
#speaker-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(125px, 1fr)); gap: 1.5rem; }
|
195 |
+
@media (min-width: 640px) { #speaker-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); } } /* Adjusted for better spacing */
|
196 |
+
.speaker-card { cursor: pointer; transition: var(--transition-smooth); text-align: center; position: relative;}
|
197 |
+
.speaker-card .speaker-visual {
|
198 |
+
border: 3px solid transparent;
|
199 |
+
border-radius: var(--radius-card);
|
200 |
+
overflow: hidden;
|
201 |
+
box-shadow: var(--shadow-subtle);
|
202 |
+
position: relative;
|
203 |
+
background-color: var(--input-bg);
|
204 |
+
transition: var(--transition-smooth);
|
205 |
+
padding: 8px; /* Added padding around image */
|
206 |
+
}
|
207 |
+
.speaker-card:hover .speaker-visual {
|
208 |
+
transform: translateY(-6px) scale(1.05);
|
209 |
+
box-shadow: var(--shadow-medium);
|
210 |
+
}
|
211 |
+
.speaker-card input[type="radio"] { display: none; }
|
212 |
+
.speaker-card img {
|
213 |
+
width: 100%; height: 125px; /* Slightly larger */
|
214 |
+
object-fit: cover; display: block;
|
215 |
+
background-color: #e0e0e0;
|
216 |
+
transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
217 |
+
border-radius: calc(var(--radius-card) - 8px); /* Inner radius */
|
218 |
+
}
|
219 |
+
.speaker-card:hover img { transform: scale(1.1); }
|
220 |
+
.speaker-card .speaker-name { padding: 1rem 0.5rem 0.2rem; font-weight: 600; font-size: 0.95em; color: var(--text-secondary); transition: color 0.2s; }
|
221 |
+
.speaker-card input[type="radio"]:checked + .speaker-visual {
|
222 |
+
border-color: var(--accent-secondary);
|
223 |
+
box-shadow: 0 0 20px -5px rgba(16, 185, 129, 0.7);
|
224 |
+
background: linear-gradient(135deg, var(--accent-secondary), var(--accent-primary-hover));
|
225 |
+
}
|
226 |
+
.speaker-card input[type="radio"]:checked + .speaker-visual img {
|
227 |
+
border: 3px solid white; /* White border for selected image */
|
228 |
+
}
|
229 |
+
.speaker-card input[type="radio"]:checked + .speaker-visual .speaker-name {
|
230 |
+
color: #fff; /* White text on gradient */
|
231 |
+
font-weight: 700;
|
232 |
+
}
|
233 |
+
|
234 |
+
/* --- Slider & Button & Output --- */
|
235 |
+
.slider-container { display: flex; align-items: center; gap: 1.5rem; }
|
236 |
+
input[type="range"] {
|
237 |
+
flex-grow: 1; -webkit-appearance: none; appearance: none;
|
238 |
+
width: 100%; height: 10px; /* Thicker track */
|
239 |
+
background: #EAF0F6; /* Lighter track bg */
|
240 |
+
border-radius: 5px; outline: none;
|
241 |
cursor: pointer;
|
242 |
+
transition: background 0.2s;
|
243 |
+
}
|
244 |
+
input[type="range"]::-webkit-slider-runnable-track {
|
245 |
+
background: linear-gradient(to right, var(--accent-secondary) 0%, var(--accent-primary) 100%);
|
246 |
+
height: 10px;
|
247 |
+
border-radius: 5px;
|
248 |
+
}
|
249 |
+
input[type="range"]::-webkit-slider-thumb {
|
250 |
+
-webkit-appearance: none; appearance: none;
|
251 |
+
width: 24px; height: 24px; /* Larger thumb */
|
252 |
+
background: #fff;
|
253 |
+
border-radius: 50%; cursor: pointer;
|
254 |
+
border: 4px solid var(--accent-primary); /* Thicker border */
|
255 |
+
box-shadow: 0 3px 8px rgba(0,0,0,0.2);
|
256 |
+
margin-top: -7px;
|
257 |
+
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
258 |
+
}
|
259 |
+
input[type="range"]::-webkit-slider-thumb:hover {
|
260 |
+
transform: scale(1.15);
|
261 |
+
box-shadow: 0 4px 10px rgba(59, 130, 246, 0.4);
|
262 |
+
}
|
263 |
+
input[type="range"]::-moz-range-thumb {
|
264 |
+
width: 24px; height: 24px;
|
265 |
+
background: #fff;
|
266 |
+
border-radius: 50%; cursor: pointer;
|
267 |
+
border: 4px solid var(--accent-primary);
|
268 |
+
box-shadow: 0 3px 8px rgba(0,0,0,0.2);
|
269 |
+
}
|
270 |
+
input[type="range"]::-moz-range-track {
|
271 |
+
background: linear-gradient(to right, var(--accent-secondary) 0%, var(--accent-primary) 100%);
|
272 |
+
height: 10px;
|
273 |
+
border-radius: 5px;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
274 |
border: none;
|
275 |
+
}
|
276 |
+
#temperature-value {
|
277 |
+
font-weight: 700; background-color: var(--input-bg);
|
278 |
+
padding: 0.6rem 1.2rem;
|
279 |
+
border-radius: 10px;
|
280 |
+
border: 1px solid var(--panel-border);
|
281 |
+
min-width: 50px; text-align: center;
|
282 |
+
color: var(--accent-primary); /* Accent color for value */
|
283 |
+
font-size: 1.05em;
|
284 |
+
box-shadow: var(--shadow-subtle);
|
285 |
+
}
|
286 |
+
|
287 |
+
#generate-btn {
|
288 |
+
width: 100%; padding: 1.2rem 1.5rem; /* More padding */
|
289 |
+
font-size: 1.35em; font-weight: 800; /* Bolder, larger */
|
290 |
+
font-family: var(--app-font);
|
291 |
+
background: linear-gradient(95deg, var(--accent-secondary) 0%, var(--accent-primary) 100%);
|
292 |
+
color: #fff;
|
293 |
+
border: none;
|
294 |
+
border-radius: var(--radius-input);
|
295 |
+
cursor: pointer;
|
296 |
+
transition: var(--transition-smooth), transform 0.15s ease-out; /* Added transform transition */
|
297 |
+
box-shadow: 0 6px 18px -5px rgba(59, 130, 246, 0.5), 0 6px 18px -5px rgba(16, 185, 129, 0.4);
|
298 |
position: relative;
|
299 |
overflow: hidden;
|
300 |
+
letter-spacing: 0.5px;
|
|
|
301 |
}
|
302 |
+
#generate-btn::before {
|
303 |
+
content: ''; position: absolute;
|
304 |
+
top: 0; left: -150%; width: 70%; height: 100%;
|
305 |
+
background: linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,0.3) 50%, rgba(255,255,255,0) 100%);
|
306 |
+
transform: skewX(-25deg);
|
307 |
+
transition: left 0.7s cubic-bezier(0.23, 1, 0.32, 1);
|
|
|
|
|
|
|
|
|
308 |
}
|
309 |
+
#generate-btn:hover::before { left: 150%; }
|
310 |
+
#generate-btn:hover:not(:disabled) {
|
311 |
+
transform: translateY(-4px) scale(1.01); /* Enhanced hover */
|
312 |
+
box-shadow: 0 10px 25px -5px rgba(59, 130, 246, 0.6), 0 10px 25px -5px rgba(16, 185, 129, 0.5);
|
313 |
}
|
314 |
+
#generate-btn:active:not(:disabled) {
|
315 |
+
transform: translateY(-1px) scale(0.99); /* Click feedback */
|
|
|
|
|
316 |
}
|
317 |
+
#generate-btn:disabled {
|
318 |
+
background: #B8C2CC;
|
319 |
+
cursor: not-allowed; box-shadow: none; color: #E8EEF3;
|
|
|
320 |
transform: none;
|
|
|
321 |
}
|
322 |
+
#generate-btn:disabled::before { display: none; }
|
323 |
+
|
324 |
+
#output-section {
|
325 |
+
margin-top: 3.5rem;
|
326 |
+
padding: 2.5rem;
|
327 |
+
background-color: var(--input-bg); /* Use input-bg for contrast */
|
328 |
+
border-radius: var(--radius-card);
|
329 |
+
min-height: 220px;
|
330 |
+
display: flex; align-items: center; justify-content: center;
|
331 |
+
flex-direction: column; gap: 1.5rem;
|
332 |
+
border: 2px dashed var(--panel-border); /* Slightly thicker dashed border */
|
333 |
+
transition: var(--transition-smooth);
|
334 |
+
position: relative;
|
335 |
+
box-shadow: var(--shadow-subtle) inset; /* Subtle inset for empty state */
|
|
|
|
|
|
|
336 |
}
|
337 |
+
#output-section.has-content { /* Style when audio is loaded */
|
338 |
+
background-color: #fff;
|
339 |
+
border-style: solid;
|
340 |
+
border-color: var(--accent-secondary);
|
341 |
+
box-shadow: 0 0 25px -5px rgba(16, 185, 129, 0.2);
|
|
|
342 |
}
|
343 |
+
#status-message { font-weight: 500; color: var(--text-secondary); text-align: center; font-size: 1.15em; }
|
344 |
+
#audio-player { width: 100%; margin-top: 1rem; display: none; border-radius: 10px; box-shadow: var(--shadow-medium); }
|
345 |
+
#audio-player::-webkit-media-controls-panel { background-color: #fff; border-radius: 10px; padding: 5px; box-shadow: var(--shadow-subtle) inset;}
|
346 |
+
#audio-player::-webkit-media-controls-play-button { color: var(--accent-primary); background-color: rgba(59, 130, 246, 0.1); border-radius: 50%; margin: 5px;}
|
347 |
+
#audio-player::-webkit-media-controls-current-time-display,
|
348 |
+
#audio-player::-webkit-media-controls-time-remaining-display { color: var(--text-secondary); font-weight: 500; }
|
349 |
+
#audio-player::-webkit-media-controls-timeline { background-color: #EAF0F6; border-radius: 5px; margin: 0 10px;}
|
350 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
351 |
|
352 |
+
/* --- انیمیشن پردازش جدید: مدار هوشمند --- */
|
353 |
#loading-animation-wrapper {
|
354 |
+
display: none;
|
355 |
flex-direction: column;
|
356 |
align-items: center;
|
357 |
justify-content: center;
|
358 |
gap: 2rem;
|
359 |
width: 100%;
|
360 |
+
min-height: 150px;
|
361 |
}
|
362 |
|
363 |
+
.orbital-loader {
|
364 |
+
width: 120px; /* Larger */
|
365 |
height: 120px;
|
366 |
position: relative;
|
367 |
+
animation: rotate-loader-orbital 12s linear infinite;
|
368 |
}
|
369 |
+
|
370 |
+
.orbit {
|
371 |
position: absolute;
|
372 |
+
top: 50%; left: 50%;
|
373 |
+
border: 2px dashed rgba(59, 130, 246, 0.3); /* Dashed orbit lines */
|
374 |
border-radius: 50%;
|
375 |
+
transform-origin: center center;
|
|
|
|
|
376 |
}
|
377 |
|
378 |
+
.orbit:nth-child(1) { width: 40px; height: 40px; margin: -20px 0 0 -20px; animation: orbit-spin 3s linear infinite reverse; }
|
379 |
+
.orbit:nth-child(2) { width: 70px; height: 70px; margin: -35px 0 0 -35px; animation: orbit-spin 4s linear infinite; }
|
380 |
+
.orbit:nth-child(3) { width: 100px; height: 100px; margin: -50px 0 0 -50px; animation: orbit-spin 5s linear infinite reverse; }
|
|
|
|
|
|
|
|
|
|
|
381 |
|
382 |
+
.orbit .satellite {
|
383 |
position: absolute;
|
384 |
+
width: 12px; height: 12px;
|
385 |
+
border-radius: 50%;
|
386 |
+
background-color: var(--accent-primary);
|
387 |
+
box-shadow: 0 0 10px var(--accent-primary), 0 0 15px var(--accent-secondary);
|
388 |
+
animation: satellite-pulse 1.5s ease-in-out infinite alternate;
|
389 |
}
|
390 |
|
391 |
+
.orbit:nth-child(1) .satellite { top: -6px; left: 50%; transform: translateX(-50%); }
|
392 |
+
.orbit:nth-child(2) .satellite { top: 50%; left: -6px; transform: translateY(-50%); background-color: var(--accent-secondary); }
|
393 |
+
.orbit:nth-child(3) .satellite { bottom: -6px; right: 50%; transform: translateX(50%); }
|
394 |
+
|
395 |
+
|
396 |
+
@keyframes rotate-loader-orbital {
|
397 |
from { transform: rotate(0deg); }
|
398 |
to { transform: rotate(360deg); }
|
399 |
}
|
400 |
+
@keyframes orbit-spin {
|
401 |
+
from { transform: rotate(0deg); }
|
402 |
+
to { transform: rotate(360deg); }
|
|
|
403 |
}
|
404 |
+
@keyframes satellite-pulse {
|
405 |
+
from { transform: scale(0.8) translate(-50%, -50%) ; opacity: 0.7; } /* Adjust translate based on initial positioning if needed */
|
406 |
+
to { transform: scale(1.2) translate(-50%, -50%); opacity: 1; }
|
|
|
407 |
}
|
408 |
+
/* Correcting pulse for satellites not centered initially */
|
409 |
+
.orbit:nth-child(1) .satellite { animation-name: satellite-pulse-1; }
|
410 |
+
@keyframes satellite-pulse-1 {
|
411 |
+
from { transform: scale(0.8) translateX(-50%); opacity: 0.7; }
|
412 |
+
to { transform: scale(1.2) translateX(-50%); opacity: 1; }
|
|
|
|
|
413 |
}
|
414 |
+
.orbit:nth-child(2) .satellite { animation-name: satellite-pulse-2; }
|
415 |
+
@keyframes satellite-pulse-2 {
|
416 |
+
from { transform: scale(0.8) translateY(-50%); opacity: 0.7; }
|
417 |
+
to { transform: scale(1.2) translateY(-50%); opacity: 1; }
|
418 |
+
}
|
419 |
+
.orbit:nth-child(3) .satellite { animation-name: satellite-pulse-3; }
|
420 |
+
@keyframes satellite-pulse-3 {
|
421 |
+
from { transform: scale(0.8) translateX(50%); opacity: 0.7; }
|
422 |
+
to { transform: scale(1.2) translateX(50%); opacity: 1; }
|
423 |
}
|
424 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
425 |
|
426 |
+
#loading-text {
|
427 |
+
font-size: 1.25em;
|
428 |
+
font-weight: 700;
|
429 |
+
color: var(--text-primary);
|
430 |
+
text-align: center;
|
431 |
+
background: linear-gradient(45deg, var(--accent-primary), var(--accent-secondary));
|
432 |
+
-webkit-background-clip: text;
|
433 |
+
-webkit-text-fill-color: transparent;
|
434 |
}
|
435 |
+
|
436 |
</style>
|
437 |
</head>
|
438 |
<body>
|
|
|
|
|
|
|
|
|
|
|
|
|
439 |
<div class="container">
|
440 |
<header class="app-header">
|
441 |
<h1>آلفا TTS Nova</h1>
|
442 |
+
<p>صدایی نو، تجربهای نوین در تبدیل متن به گفتار با هوش مصنوعی پیشرفته</p>
|
443 |
</header>
|
444 |
|
445 |
<main class="main-content">
|
|
|
448 |
<label for="text-input">📝 متن مورد نظر شما</label>
|
449 |
<textarea id="text-input" rows="4" placeholder="متن خود را اینجا وارد کنید...">سلام دنیا! این یک نمونه متن برای آزمایش نسل جدید تبدیل متن به صدا آلفا نوا است. امیدوارم از کیفیت آن لذت ببرید.</textarea>
|
450 |
</div>
|
|
|
451 |
<div class="form-group">
|
452 |
+
<label for="prompt-input">🗣️ توصیف سبک و لحن گفتار (اختیاری)</label>
|
453 |
<input type="text" id="prompt-input" value="با صدایی شفاف، دوستانه و پرانرژی." placeholder="مثال: با لحنی رسمی و موقر، یا شاد و کودکانه">
|
454 |
</div>
|
455 |
|
|
|
460 |
<img id="selected-speaker-img" src="" alt="عکس گوینده">
|
461 |
<div id="selected-speaker-info">
|
462 |
<h3 id="selected-speaker-name"></h3>
|
463 |
+
<p id="selected-speaker-desc">گوینده پیشفرض</p>
|
464 |
</div>
|
465 |
</div>
|
466 |
+
<!-- دکمه مشاهده همه گویندگان حذف شد، عملکرد به کارت منتقل شد -->
|
467 |
</div>
|
468 |
</div>
|
469 |
|
|
|
481 |
<div id="output-section">
|
482 |
<div id="status-message">صدای تولید شده در اینجا نمایش داده خواهد شد.</div>
|
483 |
<div id="loading-animation-wrapper">
|
484 |
+
<div class="orbital-loader">
|
485 |
+
<div class="orbit">
|
486 |
+
<div class="satellite"></div>
|
487 |
+
</div>
|
488 |
+
<div class="orbit">
|
489 |
+
<div class="satellite"></div>
|
490 |
+
</div>
|
491 |
+
<div class="orbit">
|
492 |
+
<div class="satellite"></div>
|
493 |
+
</div>
|
494 |
</div>
|
495 |
<p id="loading-text">در حال پردازش هوشمند و تولید صدا...</p>
|
496 |
</div>
|
|
|
503 |
<div class="modal-content">
|
504 |
<div class="modal-header">
|
505 |
<h2>گالری گویندگان آلفا نوا</h2>
|
506 |
+
<button type="button" class="close-modal-btn" aria-label="بستن مودال">×</button>
|
507 |
</div>
|
508 |
<div id="speaker-grid"></div>
|
509 |
</div>
|
|
|
513 |
|
514 |
<script>
|
515 |
document.addEventListener('DOMContentLoaded', () => {
|
516 |
+
const HF_SPACE_URL = "https://hamed744-ttspro.hf.space"; // یا هر آدرسی که دارید
|
517 |
+
const JOIN_QUEUE_URL = `${HF_SPACE_URL}/gradio_api/queue/join`;
|
518 |
+
const GET_DATA_URL_BASE = `${HF_SPACE_URL}/gradio_api/queue/data`;
|
519 |
+
const FILE_URL_BASE = `${HF_SPACE_URL}/gradio_api/file=`;
|
520 |
+
const FN_INDEX = 1;
|
521 |
+
|
522 |
+
const speakers = [
|
523 |
+
{ id: "Charon", name: "شهاب (مرد)", desc: "صدایی قدرتمند و رسا" },
|
524 |
+
{ id: "Zephyr", name: "آوا (زن)", desc: "لطیف و دلنشین" },
|
525 |
+
{ id: "Achird", name: "نوید (مرد)", desc: "جوان و پرانرژی" },
|
526 |
+
{ id: "Zubenelgenubi", name: "رویا (زن)", desc: "گرم و صمیمی" },
|
527 |
+
{ id: "Vindemiatrix", name: "کیان (مرد)", desc: "باوقار و رسمی" },
|
528 |
+
{ id: "Sadachbia", name: "پریسا (زن)", desc: "شاداب و پویا" },
|
529 |
+
{ id: "Sadaltager", name: "آرش (مرد)", desc: "مطمئن و تاثیرگذار" },
|
530 |
+
{ id: "Sulafat", name: "شبنم (زن)", desc: "آرام و متین" },
|
531 |
+
{ id: "Laomedeia", name: "سهیل (مرد)", desc: "دوستانه و گیرا" },
|
532 |
+
{ id: "Achernar", name: "مریم (زن)", desc: "حرفهای و واضح" },
|
533 |
+
{ id: "Alnilam", name: "بهرام (مرد)", desc: "حماسی و نافذ" },
|
534 |
+
{ id: "Schedar", name: "نگار (زن)", desc: "مهربان و شیرین" },
|
535 |
+
{ id: "Gacrux", name: "فرید (مرد)", desc: "پخته و قابل اعتماد" },
|
536 |
+
{ id: "Pulcherrima", name: "سارا (زن)", desc: "جذاب و مدرن" },
|
537 |
+
{ id: "Umbriel", name: "مانی (مرد)", desc: "خلاق و متفاوت" },
|
538 |
+
{ id: "Algieba", name: "آناهیتا (زن)", desc: "با اصالت و شیک" },
|
539 |
+
{ id: "Despina", name: "دلنواز (زن)", desc: "هنری و احساسی" },
|
540 |
+
{ id: "Erinome", name: "رسا (مرد)", desc: "شفاف و گویا" },
|
541 |
+
{ id: "Algenib", name: "امید (مرد)", desc: "انگیزه بخش و مثبت" },
|
542 |
+
{ id: "Rasalthgeti", name: "الهه (زن)", desc: "اسرارآمیز و فریبنده" },
|
543 |
+
{ id: "Orus", name: "بردیا (مرد)", desc: "ورزشی و پرهیجان" },
|
544 |
+
{ id: "Aoede", name: "ترانه (زن)", desc: "موزیکال و خوشآهنگ" },
|
545 |
+
{ id: "Callirrhoe", name: "نیما (مرد)", desc: "روایتگر و قصهگو" },
|
546 |
+
{ id: "Autonoe", name: "هستی (زن)", desc: "طبیعی و خودمانی" },
|
547 |
+
{ id: "Enceladus", name: "کامیار (مرد)", desc: "مصمم و جدی" },
|
548 |
+
{ id: "Iapetus", name: "ستاره (زن)", desc: "درخشان و گیرا" },
|
549 |
+
{ id: "Puck", name: "پویا (مرد)", desc: "بازیگوش و سرزنده" },
|
550 |
+
{ id: "Kore", name: "مهتاب (زن)", desc: "نجواگر و آرامشبخش" },
|
551 |
+
{ id: "Fenrir", name: "سام (مرد)", desc: "جسور و بیباک" },
|
552 |
+
{ id: "Leda", name: "لیدا (زن)", desc: "کلاسیک و باوقار" }
|
553 |
+
];
|
554 |
+
|
555 |
+
const form = document.getElementById('tts-form');
|
556 |
+
const textInput = document.getElementById('text-input');
|
557 |
+
const promptInput = document.getElementById('prompt-input');
|
558 |
+
const tempSlider = document.getElementById('temperature-slider');
|
559 |
+
const tempValueSpan = document.getElementById('temperature-value');
|
560 |
+
const generateBtn = document.getElementById('generate-btn');
|
561 |
+
|
562 |
+
const outputSection = document.getElementById('output-section');
|
563 |
+
const statusMessage = document.getElementById('status-message');
|
564 |
+
const audioPlayer = document.getElementById('audio-player');
|
565 |
+
const loadingAnimationWrapper = document.getElementById('loading-animation-wrapper');
|
566 |
+
|
567 |
+
const selectedSpeakerIdStorage = document.getElementById('selected_speaker_id_storage');
|
568 |
+
const speakerModal = document.getElementById('speaker-modal');
|
569 |
+
// const changeSpeakerBtn = document.getElementById('change-speaker-btn'); // No longer needed
|
570 |
+
const closeModalBtn = document.querySelector('.close-modal-btn');
|
571 |
+
const speakerGridInModal = document.getElementById('speaker-grid');
|
572 |
+
const selectedSpeakerImgDisplay = document.getElementById('selected-speaker-img');
|
573 |
+
const selectedSpeakerNameDisplay = document.getElementById('selected-speaker-name');
|
574 |
+
const selectedSpeakerDescDisplay = document.getElementById('selected-speaker-desc');
|
575 |
+
const selectedSpeakerCard = document.getElementById('selected-speaker-card');
|
576 |
+
|
577 |
+
function getSpeakerById(id) {
|
578 |
+
return speakers.find(s => s.id === id) || speakers[0]; // Fallback to first speaker
|
579 |
+
}
|
580 |
+
|
581 |
+
function getImageUrl(speaker, index, size = 'medium') {
|
582 |
+
const gender = speaker.name.includes('(مرد)') ? 'men' : (speaker.name.includes('(زن)') ? 'women' : 'lego');
|
583 |
+
const imageIndex = (index * 13 + 7 + speaker.id.length) % 100; // More variation
|
584 |
+
let portraitSizePath = 'thumb/';
|
585 |
+
if (size === 'large') portraitSizePath = '';
|
586 |
+
|
587 |
+
return `https://randomuser.me/api/portraits/${portraitSizePath}${gender}/${imageIndex}.jpg`;
|
588 |
+
}
|
589 |
+
|
590 |
+
function updateSelectedSpeakerDisplay(speakerId) {
|
591 |
+
const speaker = getSpeakerById(speakerId);
|
592 |
+
const speakerIndex = speakers.findIndex(s => s.id === speaker.id);
|
593 |
+
selectedSpeakerImgDisplay.src = getImageUrl(speaker, speakerIndex, 'large');
|
594 |
+
selectedSpeakerImgDisplay.alt = `عکس گوینده ${speaker.name}`;
|
595 |
+
selectedSpeakerNameDisplay.textContent = speaker.name;
|
596 |
+
selectedSpeakerDescDisplay.textContent = speaker.desc || "گوینده منتخب شما";
|
597 |
+
selectedSpeakerIdStorage.value = speaker.id;
|
598 |
+
}
|
599 |
+
|
600 |
+
function createSpeakerCardsInModal() {
|
601 |
+
speakerGridInModal.innerHTML = '';
|
602 |
+
speakers.forEach((speaker, index) => {
|
603 |
+
const cardLabel = document.createElement('label');
|
604 |
+
cardLabel.className = 'speaker-card';
|
605 |
+
cardLabel.setAttribute('for', `modal-speaker-${speaker.id}`);
|
606 |
+
const isChecked = speaker.id === selectedSpeakerIdStorage.value ? 'checked' : '';
|
607 |
+
|
608 |
+
cardLabel.innerHTML = `
|
609 |
+
<input type="radio" name="modal_speaker_selection" value="${speaker.id}" id="modal-speaker-${speaker.id}" ${isChecked}>
|
610 |
+
<div class="speaker-visual">
|
611 |
+
<img src="${getImageUrl(speaker, index, 'thumb')}" alt="${speaker.name}" loading="lazy">
|
612 |
+
<div class="speaker-name">${speaker.name}</div>
|
613 |
+
</div>
|
614 |
+
`;
|
615 |
+
|
616 |
+
// Event listener on the label itself
|
617 |
+
cardLabel.addEventListener('click', (e) => {
|
618 |
+
// Check if the click was on the input or something inside,
|
619 |
+
// as the input itself will trigger its own change/click
|
620 |
+
if (e.target.name !== "modal_speaker_selection") {
|
621 |
+
const radio = cardLabel.querySelector('input[type="radio"]');
|
622 |
+
if(radio) radio.checked = true; // Manually check if not already
|
623 |
+
}
|
624 |
+
updateSelectedSpeakerDisplay(speaker.id);
|
625 |
+
setTimeout(() => speakerModal.classList.remove('visible'), 250); // Slight delay for visual feedback
|
626 |
+
});
|
627 |
+
|
628 |
+
speakerGridInModal.appendChild(cardLabel);
|
629 |
+
});
|
630 |
+
}
|
631 |
+
|
632 |
+
function openSpeakerModal() {
|
633 |
+
createSpeakerCardsInModal(); // Refresh cards each time in case selection changed elsewhere
|
634 |
+
speakerModal.classList.add('visible');
|
635 |
+
// Focus on the first selectable item or the modal itself for accessibility
|
636 |
+
setTimeout(() => { // Timeout for transition
|
637 |
+
const firstFocusable = speakerModal.querySelector('input[type="radio"], .close-modal-btn');
|
638 |
+
if (firstFocusable) firstFocusable.focus();
|
639 |
+
else closeModalBtn.focus();
|
640 |
+
}, 50);
|
641 |
+
}
|
642 |
+
|
643 |
+
// changeSpeakerBtn.addEventListener('click', openSpeakerModal); // Removed
|
644 |
+
selectedSpeakerCard.addEventListener('click', openSpeakerModal);
|
645 |
+
|
646 |
+
closeModalBtn.addEventListener('click', () => speakerModal.classList.remove('visible'));
|
647 |
+
speakerModal.addEventListener('click', (e) => {
|
648 |
+
if (e.target === speakerModal) {
|
649 |
+
speakerModal.classList.remove('visible');
|
650 |
+
}
|
651 |
+
});
|
652 |
+
// Keyboard accessibility for modal
|
653 |
+
document.addEventListener('keydown', (e) => {
|
654 |
+
if (e.key === 'Escape' && speakerModal.classList.contains('visible')) {
|
655 |
+
speakerModal.classList.remove('visible');
|
656 |
+
}
|
657 |
+
});
|
658 |
+
|
659 |
+
tempSlider.addEventListener('input', () => { tempValueSpan.textContent = tempSlider.value; });
|
660 |
+
|
661 |
+
function showLoadingState() {
|
662 |
+
statusMessage.style.display = 'none';
|
663 |
+
audioPlayer.style.display = 'none';
|
664 |
+
audioPlayer.src = '';
|
665 |
+
outputSection.classList.remove('has-content');
|
666 |
+
loadingAnimationWrapper.style.display = 'flex';
|
667 |
+
generateBtn.disabled = true;
|
668 |
+
generateBtn.innerHTML = `
|
669 |
+
<svg aria-hidden="true" style="display:inline-block; margin-left: 0.5em; width:1.2em; height:1.2em;" role="status" fill="currentColor" viewBox="0 0 100 101">
|
670 |
+
<path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="#E5E7EB"/>
|
671 |
+
<path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0492C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentColor"/>
|
672 |
+
</svg>
|
673 |
+
در حال پردازش...
|
674 |
+
`;
|
675 |
+
}
|
676 |
+
|
677 |
+
function showResultState(isSuccess, message = '') {
|
678 |
+
loadingAnimationWrapper.style.display = 'none';
|
679 |
+
if (isSuccess) {
|
680 |
+
statusMessage.style.display = 'none';
|
681 |
+
audioPlayer.style.display = 'block';
|
682 |
+
outputSection.classList.add('has-content');
|
683 |
+
// audioPlayer.play(); // Consider UX for autoplay
|
684 |
+
} else {
|
685 |
+
statusMessage.textContent = message || 'خطایی رخ داد. لطفاً دوباره تلاش کنید.';
|
686 |
+
statusMessage.style.display = 'block';
|
687 |
+
audioPlayer.style.display = 'none';
|
688 |
+
outputSection.classList.remove('has-content');
|
689 |
+
}
|
690 |
+
generateBtn.disabled = false;
|
691 |
+
generateBtn.textContent = '🚀 تولید صدا با آلفا نوا';
|
692 |
+
}
|
693 |
+
|
694 |
+
async function generateAudio(event) {
|
695 |
+
event.preventDefault();
|
696 |
+
showLoadingState();
|
697 |
+
|
698 |
+
const text = textInput.value;
|
699 |
+
if (!text.trim()) {
|
700 |
+
showResultState(false, 'خطا: متن ورودی نمیتواند خالی باشد.');
|
701 |
+
return;
|
702 |
+
}
|
703 |
+
|
704 |
+
const promptVal = promptInput.value;
|
705 |
+
const temperatureVal = parseFloat(tempSlider.value);
|
706 |
+
const selectedSpeakerVal = selectedSpeakerIdStorage.value;
|
707 |
+
const sessionHash = Math.random().toString(36).substring(2);
|
708 |
+
|
709 |
+
const payload = {
|
710 |
+
fn_index: FN_INDEX,
|
711 |
+
data: [false, null, text, promptVal, selectedSpeakerVal, temperatureVal],
|
712 |
+
event_data: null,
|
713 |
+
session_hash: sessionHash
|
714 |
+
};
|
715 |
+
|
716 |
+
try {
|
717 |
+
const joinQueueResponse = await fetch(JOIN_QUEUE_URL, {
|
718 |
+
method: "POST",
|
719 |
+
headers: { "Content-Type": "application/json" },
|
720 |
+
body: JSON.stringify(payload)
|
721 |
+
});
|
722 |
+
|
723 |
+
if (!joinQueueResponse.ok) {
|
724 |
+
const errorBody = await joinQueueResponse.text();
|
725 |
+
console.error("Join Queue Error Body:", errorBody);
|
726 |
+
throw new Error(`خطا در برقراری ارتباط با سرویس آلفا (${joinQueueResponse.status}). لطفا لحظاتی دیگر تلاش کنید.`);
|
727 |
+
}
|
728 |
+
|
729 |
+
// Start polling for data
|
730 |
+
let finalFilePath = null;
|
731 |
+
const startTime = Date.now();
|
732 |
+
const timeoutDuration = 90000; // 90 seconds timeout for the whole process
|
733 |
+
|
734 |
+
while (Date.now() - startTime < timeoutDuration) {
|
735 |
+
const dataResponse = await fetch(`${GET_DATA_URL_BASE}?session_hash=${sessionHash}`);
|
736 |
+
if (!dataResponse.ok) {
|
737 |
+
const errorBody = await dataResponse.text();
|
738 |
+
console.error("Get Data Error Body:", errorBody);
|
739 |
+
throw new Error(`خطا در دریافت داده از سرویس (${dataResponse.status})`);
|
740 |
+
}
|
741 |
+
|
742 |
+
const responseText = await dataResponse.text();
|
743 |
+
const lines = responseText.trim().split('\n');
|
744 |
+
|
745 |
+
for (const line of lines) {
|
746 |
+
if (!line.startsWith('data:')) continue;
|
747 |
+
try {
|
748 |
+
const data = JSON.parse(line.substring(5));
|
749 |
+
if (data.msg === 'process_generating' || data.msg === 'process_starts') {
|
750 |
+
// Optionally update loading text here if server provides progress
|
751 |
+
// console.log("Processing:", data.output?.progress_data?.[0]?.desc || data.msg);
|
752 |
+
} else if (data.msg === 'process_completed') {
|
753 |
+
if (data.success && data.output.data && data.output.data[0] && (data.output.data[0].name || data.output.data[0].path)) {
|
754 |
+
finalFilePath = data.output.data[0].name || data.output.data[0].path;
|
755 |
+
} else {
|
756 |
+
console.error("ساختار داده پاسخ سرور معتبر نیست یا پردازش ناموفق بود:", data);
|
757 |
+
throw new Error(data.output?.error || 'خطای ناشناخته در پردازش سرور.');
|
758 |
+
}
|
759 |
+
break; // Exit inner loop
|
760 |
+
} else if (data.msg === 'queue_full') {
|
761 |
+
throw new Error('صف پردازش پر است. لطفا کمی بعد تلاش کنید.');
|
762 |
+
}
|
763 |
+
} catch (e) {
|
764 |
+
console.warn("خطا در تجزیه JSON از stream:", e, "Line:", line);
|
765 |
+
}
|
766 |
+
}
|
767 |
+
if (finalFilePath) break; // Exit outer loop
|
768 |
+
await new Promise(resolve => setTimeout(resolve, 1000)); // Poll every 1 second
|
769 |
+
}
|
770 |
+
|
771 |
+
if (finalFilePath) {
|
772 |
+
const audioUrl = `${FILE_URL_BASE}${finalFilePath}`;
|
773 |
+
audioPlayer.src = audioUrl;
|
774 |
+
showResultState(true);
|
775 |
+
} else if (Date.now() - startTime >= timeoutDuration) {
|
776 |
+
throw new Error('پردازش بیش از حد طول کشید و متوقف شد.');
|
777 |
+
} else {
|
778 |
+
throw new Error('فایل صوتی از سرور دریافت نشد یا پردازش ناموفق بود.');
|
779 |
+
}
|
780 |
+
|
781 |
+
} catch (error) {
|
782 |
+
console.error('خطا در فرآیند تولید صدا:', error);
|
783 |
+
showResultState(false, `${error.message}`);
|
784 |
+
}
|
785 |
+
}
|
786 |
+
|
787 |
+
// Initial setup
|
788 |
+
updateSelectedSpeakerDisplay(selectedSpeakerIdStorage.value || speakers[0].id); // Ensure a default is set
|
789 |
+
form.addEventListener('submit', generateAudio);
|
790 |
+
|
791 |
+
statusMessage.style.display = 'block';
|
792 |
+
loadingAnimationWrapper.style.display = 'none';
|
793 |
+
audioPlayer.style.display = 'none';
|
794 |
+
outputSection.classList.remove('has-content');
|
795 |
+
});
|
796 |
+
</script>
|
797 |
+
</body>
|
798 |
+
</html>
|