File size: 34,407 Bytes
e4d0150
d29f80b
3b52ce7
3012bdb
3b52ce7
 
3012bdb
d29f80b
150c009
d29f80b
3012bdb
d29f80b
e4d0150
1799f89
 
 
 
 
d29f80b
3b52ce7
d29f80b
 
 
3b52ce7
d29f80b
e4d0150
 
 
 
 
 
 
d29f80b
eb51a4d
3b52ce7
 
eb51a4d
6fd3544
3012bdb
d29f80b
a17f9d2
3012bdb
e4d0150
674a927
3012bdb
e4d0150
 
3012bdb
e4d0150
2757c04
1799f89
ec42d41
674a927
 
 
 
 
b62be89
2757c04
674a927
 
 
ec42d41
 
eb51a4d
d29f80b
a17f9d2
674a927
d29f80b
674a927
ec42d41
3012bdb
eb51a4d
 
d29f80b
1799f89
eb51a4d
 
e4d0150
47095d4
d29f80b
 
 
 
 
 
 
 
 
 
 
 
eb51a4d
d29f80b
eb51a4d
6fd3544
3012bdb
a17f9d2
e4bf7cb
3012bdb
3b52ce7
d29f80b
3b52ce7
1799f89
3012bdb
1799f89
674a927
3b52ce7
d29f80b
150c009
 
 
 
d29f80b
 
6fd3544
d29f80b
 
 
 
150c009
47095d4
3012bdb
3b52ce7
 
d29f80b
3b52ce7
eb51a4d
d29f80b
b40ae8b
3012bdb
 
 
d29f80b
3b52ce7
d29f80b
 
3b52ce7
150c009
d29f80b
3012bdb
150c009
d29f80b
6fd3544
3b52ce7
 
3012bdb
6fd3544
3012bdb
3b52ce7
3012bdb
6fd3544
3012bdb
3b52ce7
e4d0150
d29f80b
3012bdb
 
d29f80b
 
 
 
3012bdb
 
d29f80b
 
 
150c009
 
3012bdb
 
 
 
d29f80b
3012bdb
 
 
 
d29f80b
3012bdb
 
d29f80b
 
 
 
 
 
 
3012bdb
6fd3544
d29f80b
 
 
 
 
150c009
3b52ce7
3012bdb
3b52ce7
 
 
3012bdb
d29f80b
 
3012bdb
3b52ce7
 
 
 
a17f9d2
3b52ce7
 
 
d29f80b
 
3012bdb
 
3b52ce7
d29f80b
6fd3544
3012bdb
d29f80b
 
 
3012bdb
6fd3544
3b52ce7
 
d29f80b
 
 
 
 
 
 
 
 
 
 
 
6fd3544
 
d29f80b
3b52ce7
 
 
d29f80b
 
 
 
 
 
 
3b52ce7
150c009
d29f80b
 
 
6fd3544
3012bdb
078eb75
3b52ce7
 
 
 
 
3012bdb
3b52ce7
 
d29f80b
 
6fd3544
3012bdb
 
 
6fd3544
3012bdb
d29f80b
3012bdb
d29f80b
 
6fd3544
674a927
e4d0150
d29f80b
e4d0150
d29f80b
6fd3544
d29f80b
6fd3544
d29f80b
3012bdb
674a927
d29f80b
3012bdb
6fd3544
d29f80b
 
 
 
3b52ce7
3012bdb
 
d29f80b
3b52ce7
 
6fd3544
3b52ce7
 
 
 
 
 
6fd3544
3b52ce7
d29f80b
3b52ce7
 
 
 
 
d29f80b
 
 
 
3b52ce7
d29f80b
3b52ce7
 
290123f
3b52ce7
 
 
 
 
d29f80b
3012bdb
3b52ce7
 
 
d29f80b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b40ae8b
d29f80b
 
 
 
3012bdb
d29f80b
 
 
6fd3544
d29f80b
 
 
3012bdb
 
d29f80b
 
 
6fd3544
d29f80b
 
 
 
6fd3544
 
d29f80b
 
 
 
 
 
 
 
 
 
 
 
3012bdb
d29f80b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3012bdb
d29f80b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6fd3544
d29f80b
3012bdb
d29f80b
 
 
 
 
 
 
47095d4
d29f80b
 
 
 
 
 
 
 
 
 
6fd3544
d29f80b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b40ae8b
3012bdb
d29f80b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3012bdb
d29f80b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290123f
d29f80b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3b52ce7
d29f80b
 
 
6fd3544
d29f80b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3012bdb
d29f80b
 
3b52ce7
6fd3544
d29f80b
 
 
6fd3544
d29f80b
 
6fd3544
d29f80b
 
 
e4d0150
18059a6
d29f80b
 
 
 
 
 
3b52ce7
 
d29f80b
 
 
6fd3544
3b52ce7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
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
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
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
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
import gradio as gr
# import base64 # Not used in your original core logic
import mimetypes
import os
import re
import struct
import time
# import zipfile # Not used in your original core logic
from google import genai
from google.genai import types as genai_types # Aliased to avoid conflict with built-in 'types'

import logging # Standard Python logging

try:
    from pydub import AudioSegment
    PYDUB_AVAILABLE = True
except ImportError:
    PYDUB_AVAILABLE = False
    # logging.warning("Pydub is not available. Audio merging will be disabled.") # Initialized later

# --- Basic Logging Setup ---
# Using a simpler logging setup if the AlphaTranslator_Styled one is too complex for "no other changes"
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# --- START: Core TTS Logic from YOUR AlphaTTS_Original (UNCHANGED) ---
SPEAKER_VOICES = [
    "Achird", "Zubenelgenubi", "Vindemiatrix", "Sadachbia", "Sadaltager",
    "Sulafat", "Laomedeia", "Achernar", "Alnilam", "Schedar", "Gacrux",
    "Pulcherrima", "Umbriel", "Algieba", "Despina", "Erinome", "Algenib",
    "Rasalthgeti", "Orus", "Aoede", "Callirrhoe", "Autonoe", "Enceladus",
    "Iapetus", "Zephyr", "Puck", "Charon", "Kore", "Fenrir", "Leda"
]
FIXED_MODEL_NAME = "gemini-2.5-flash-preview-tts" # FROM YOUR ORIGINAL CODE
DEFAULT_MAX_CHUNK_SIZE = 3800
DEFAULT_SLEEP_BETWEEN_REQUESTS = 8
DEFAULT_OUTPUT_FILENAME_BASE = "alpha_tts_audio"

def _log(message, log_list): # YOUR _log function
    log_list.append(message)
    logging.info(f"[AlphaTTS_LOG] {message}") # Standard logging also

def save_binary_file(file_name, data, log_list):
    try:
        with open(file_name, "wb") as f: f.write(data)
        _log(f"✅ فایل ذخیره شد: {file_name}", log_list)
        return file_name
    except Exception as e:
        _log(f"❌ خطا در ذخیره فایل {file_name}: {e}", log_list)
        return None

def convert_to_wav(audio_data: bytes, mime_type: str) -> bytes:
    parameters = parse_audio_mime_type(mime_type)
    bits_per_sample, rate = parameters["bits_per_sample"], parameters["rate"]
    num_channels, data_size = 1, len(audio_data)
    bytes_per_sample, block_align = bits_per_sample // 8, num_channels * (bits_per_sample // 8)
    byte_rate, chunk_size = rate * block_align, 36 + data_size
    header = struct.pack("<4sI4s4sIHHIIHH4sI", b"RIFF", chunk_size, b"WAVE", b"fmt ", 16, 1, num_channels, rate, byte_rate, block_align, bits_per_sample, b"data", data_size)
    return header + audio_data

def parse_audio_mime_type(mime_type: str) -> dict[str, int]:
    bits, rate = 16, 24000
    for param in mime_type.split(";"):
        param = param.strip()
        if param.lower().startswith("rate="):
            try: rate = int(param.split("=", 1)[1])
            except ValueError: pass
        elif param.startswith("audio/L"):
            try: bits = int(param.split("L", 1)[1])
            except ValueError: pass
    return {"bits_per_sample": bits, "rate": rate}

def smart_text_split(text, max_size=3800, log_list=None):
    if len(text) <= max_size: return [text]
    chunks, current_chunk = [], ""
    sentences = re.split(r'(?<=[.!?؟۔])\s+', text) # Added Persian full stop for robustness
    for sentence in sentences:
        if len(current_chunk) + len(sentence) + 1 > max_size:
            if current_chunk: chunks.append(current_chunk.strip())
            current_chunk = sentence
            while len(current_chunk) > max_size:
                split_idx = -1
                for punc in ['،', ',', ';', ':', ' ']:
                    try:
                        idx = current_chunk.rindex(punc, max_size // 2, max_size)
                        if idx > split_idx:
                           split_idx = idx
                    except ValueError:
                        pass
                if split_idx != -1 :
                    part, current_chunk = current_chunk[:split_idx+1], current_chunk[split_idx+1:]
                else:
                    part, current_chunk = current_chunk[:max_size], current_chunk[max_size:]
                chunks.append(part.strip())
        else: current_chunk += (" " if current_chunk and sentence else "") + sentence
    if current_chunk: chunks.append(current_chunk.strip())
    final_chunks = [c for c in chunks if c]
    if log_list: _log(f"📊 متن به {len(final_chunks)} قطعه تقسیم شد.", log_list)
    return final_chunks

def merge_audio_files_func(file_paths, output_path, log_list):
    if not PYDUB_AVAILABLE:
        _log("❌ pydub در دسترس نیست. ادغام انجام نشد.", log_list)
        return False
    try:
        _log(f"🔗 ادغام {len(file_paths)} فایل صوتی...", log_list)
        combined = AudioSegment.empty()
        for i, fp in enumerate(file_paths):
            if os.path.exists(fp):
                try:
                    segment = AudioSegment.from_file(fp)
                    combined += segment
                    if i < len(file_paths) - 1:
                        combined += AudioSegment.silent(duration=150)
                except Exception as e_pydub:
                    _log(f"⚠️ خطای Pydub در پردازش فایل '{fp}': {e_pydub}. از این فایل صرف نظر می شود.", log_list)
                    continue
            else:
                _log(f"⚠️ فایل پیدا نشد: {fp}", log_list)
        if len(combined) == 0:
            _log("❌ هیچ قطعه صوتی برای ادغام وجود ندارد.", log_list)
            return False
        combined.export(output_path, format="wav")
        _log(f"✅ فایل ادغام شده: {output_path}", log_list)
        return True
    except Exception as e:
        _log(f"❌ خطا در ادغام: {e}", log_list) # traceback.format_exc() removed to keep it closer to original
        return False

def core_generate_audio(text_input, prompt_input, selected_voice, temperature_val, log_list): # YOUR core_generate_audio
    output_base_name = DEFAULT_OUTPUT_FILENAME_BASE
    max_chunk, sleep_time = DEFAULT_MAX_CHUNK_SIZE, DEFAULT_SLEEP_BETWEEN_REQUESTS
    _log(f"🚀 شروع فرآیند با مدل: {FIXED_MODEL_NAME}...", log_list)
    
    api_key = os.environ.get("GEMINI_API_KEY") # YOUR WAY OF GETTING API KEY
    if not api_key:
        _log("❌ کلید API با نام GEMINI_API_KEY در متغیرهای محیطی تنظیم نشده.", log_list)
        return None # Return None only, as per your original AlphaTTS

    try:
        client = genai.Client(api_key=api_key) # YOUR WAY OF CLIENT INSTANTIATION
        _log(f"کلاینت Gemini با کلید API برای مدل {FIXED_MODEL_NAME} مقداردهی اولیه شد.", log_list)
    except Exception as e:
        _log(f"❌ خطا در مقداردهی اولیه کلاینت Gemini: {e}", log_list)
        return None

    if not text_input or not text_input.strip():
        _log("❌ متن ورودی خالی.", log_list)
        return None
    text_chunks = smart_text_split(text_input, max_chunk, log_list)
    if not text_chunks:
        _log("❌ متن قابل پردازش نیست.", log_list)
        return None
    
    generated_files = []
    for i, chunk in enumerate(text_chunks):
        _log(f"🔊 پردازش قطعه {i+1}/{len(text_chunks)} (صدا: {selected_voice}, دما: {temperature_val})...", log_list)
        final_text = f'"{prompt_input}"\n{chunk}' if prompt_input and prompt_input.strip() else chunk
        
        # Using genai_types (aliased) for Content, Part etc. as in your original imports
        contents = [genai_types.Content(role="user", parts=[genai_types.Part.from_text(text=final_text)])]
        
        config = genai_types.GenerateContentConfig( # YOUR CONFIG OBJECT
            temperature=temperature_val,
            response_modalities=["audio"],
            speech_config=genai_types.SpeechConfig(
                voice_config=genai_types.VoiceConfig(
                    prebuilt_voice_config=genai_types.PrebuiltVoiceConfig(voice_name=selected_voice)
                )
            )
        )
        _log(f"کانفیگ API برای قطعه {i+1}: دما={temperature_val}, صدا={selected_voice}, مدالیته=['audio']", log_list)
        fname_base = f"{output_base_name}_part{i+1:03d}"
        try:
            # YOUR API CALL
            response = client.models.generate_content(model=FIXED_MODEL_NAME, contents=contents, config=config)
            
            if response.candidates and response.candidates[0].content and response.candidates[0].content.parts and response.candidates[0].content.parts[0].inline_data:
                inline_data = response.candidates[0].content.parts[0].inline_data
                data_buffer = inline_data.data
                mime_type = inline_data.mime_type
                _log(f"داده صوتی در candidate.part[0].inline_data برای قطعه {i+1} یافت شد.", log_list)
                ext = mimetypes.guess_extension(mime_type) or ".wav"
                if "audio/L" in mime_type and ext == ".wav":
                    _log(f"تبدیل صدای خام PCM (MIME: {mime_type}) به WAV برای قطعه {i+1}.", log_list)
                    data_buffer = convert_to_wav(data_buffer, mime_type)
                if not ext.startswith("."): ext = "." + ext
                fpath = save_binary_file(f"{fname_base}{ext}", data_buffer, log_list)
                if fpath: generated_files.append(fpath)
            else:
                _log(f"⚠️ پاسخ API برای قطعه {i+1} بدون داده صوتی در مسیر مورد انتظار.", log_list)
                _log(f"ساختار کامل پاسخ (اولین 500 کاراکتر): {str(response)[:500]}", log_list)
                # continue # As per your original code, it continues
        except Exception as e: # Catching generic Exception as in your original
            _log(f"❌ خطا در تولید قطعه {i+1}: {e}", log_list)
            # traceback.format_exc() was not in your original core_generate_audio, so removed here
            continue
        if i < len(text_chunks) - 1 and len(text_chunks) > 1:
            _log(f"💤 توقف کوتاه ({sleep_time} ثانیه) قبل از قطعه بعدی...", log_list)
            time.sleep(sleep_time)

    if not generated_files:
        _log("❌ هیچ فایلی تولید نشد.", log_list)
        return None # Return None only as per your original AlphaTTS
        
    _log(f"🎉 {len(generated_files)} فایل(های) صوتی تولید شد.", log_list)
    final_audio_file = None
    final_output_path_base = f"{output_base_name}_final"

    if len(generated_files) > 1:
        if PYDUB_AVAILABLE:
            merged_fn = f"{final_output_path_base}.wav"
            if os.path.exists(merged_fn):
                try: os.remove(merged_fn)
                except OSError: _log(f"⚠️ عدم امکان حذف فایل ادغام شده قبلی '{merged_fn}' (خطای سیستم عامل)", log_list)
                except Exception as e_rm: _log(f"⚠️ عدم امکان حذف فایل ادغام شده قبلی '{merged_fn}': {e_rm}", log_list)
            
            if merge_audio_files_func(generated_files, merged_fn, log_list):
                final_audio_file = merged_fn
                for fp_path in generated_files:
                    if os.path.abspath(fp_path) != os.path.abspath(merged_fn):
                        try: os.remove(fp_path)
                        except OSError: _log(f"⚠️ عدم امکان حذف فایل موقت '{fp_path}' (خطای سیستم عامل)", log_list)
                        except Exception as e_del: _log(f"⚠️ عدم امکان حذف فایل موقت '{fp_path}': {e_del}", log_list)
            else:
                _log("⚠️ ادغام فایل‌های صوتی ناموفق بود. اولین قطعه ارائه می‌شود.", log_list)
                if generated_files:
                    try:
                        first_chunk_path = generated_files[0]
                        target_ext = os.path.splitext(first_chunk_path)[1]
                        fallback_fn = f"{final_output_path_base}{target_ext}" # Simplified name for fallback
                        if os.path.exists(fallback_fn) and os.path.abspath(first_chunk_path) != os.path.abspath(fallback_fn):
                            os.remove(fallback_fn)
                        if os.path.abspath(first_chunk_path) != os.path.abspath(fallback_fn):
                           os.rename(first_chunk_path, fallback_fn)
                        final_audio_file = fallback_fn
                        for i_gf in range(1, len(generated_files)):
                             try: os.remove(generated_files[i_gf])
                             except: pass # Keep silent as per your original
                    except Exception as e_rename_fb:
                         _log(f"خطا در تغییر نام فایل اولین قطعه: {e_rename_fb}", log_list) # Was `e_rename` in your original
                         final_audio_file = generated_files[0]
        else: 
            _log("⚠️ pydub نیست. اولین قطعه ارائه می‌شود.", log_list)
            if generated_files:
                try:
                    first_chunk_path = generated_files[0]
                    target_ext = os.path.splitext(first_chunk_path)[1]
                    single_fallback_fn = f"{final_output_path_base}{target_ext}" # Simplified name
                    if os.path.exists(single_fallback_fn) and os.path.abspath(first_chunk_path) != os.path.abspath(single_fallback_fn):
                        os.remove(single_fallback_fn)
                    if os.path.abspath(first_chunk_path) != os.path.abspath(single_fallback_fn):
                        os.rename(first_chunk_path, single_fallback_fn)
                    final_audio_file = single_fallback_fn
                    for i_gf in range(1, len(generated_files)):
                        try: os.remove(generated_files[i_gf])
                        except: pass # Keep silent
                except Exception as e_rename_single_npd: # Was `e_rename_single` in your original
                    _log(f"خطا در تغییر نام فایل اولین قطعه (بدون pydub): {e_rename_single_npd}", log_list)
                    final_audio_file = generated_files[0]
    elif len(generated_files) == 1:
        try:
            single_file_path = generated_files[0]
            target_ext = os.path.splitext(single_file_path)[1]
            final_single_fn = f"{final_output_path_base}{target_ext}"
            if os.path.exists(final_single_fn) and os.path.abspath(single_file_path) != os.path.abspath(final_single_fn):
                 os.remove(final_single_fn)
            if os.path.abspath(single_file_path) != os.path.abspath(final_single_fn):
                 os.rename(single_file_path, final_single_fn)
            final_audio_file = final_single_fn
        except Exception as e_rename_sgl_final: # Was `e_rename_single_final` in your original
            _log(f"خطا در تغییر نام فایل تکی نهایی: {e_rename_sgl_final}", log_list)
            final_audio_file = generated_files[0]
    
    if final_audio_file and not os.path.exists(final_audio_file): 
        _log(f"⚠️ فایل نهایی '{final_audio_file}' وجود ندارد!", log_list)
        return None
        
    return final_audio_file # Returns only path, as per your original AlphaTTS

# Your original gradio_tts_interface
def gradio_tts_interface(use_file_input, uploaded_file, text_to_speak, speech_prompt, speaker_voice, temperature, progress=gr.Progress(track_tqdm=True)):
    logs = []
    actual_text = ""
    if use_file_input:
        if uploaded_file:
            try:
                # In Gradio, uploaded_file.name is the path to the temporary file
                with open(uploaded_file.name, 'r', encoding='utf-8') as f: actual_text = f.read().strip()
                if not actual_text: return None # Return None only, as per your original
            except Exception as e: _log(f"❌ خطا خواندن فایل: {e}", logs); return None
        else: return None
    else:
        actual_text = text_to_speak
        if not actual_text or not actual_text.strip(): return None
    
    final_path = core_generate_audio(actual_text, speech_prompt, speaker_voice, temperature, logs)
    # Your original code commented out printing logs here, so I'll keep it commented.
    # for log_entry in logs: print(log_entry) # For debugging in HF console
    return final_path # Returns only path, as per your original AlphaTTS
# --- END: Core TTS Logic from YOUR AlphaTTS_Original (UNCHANGED) ---


# --- START: Gradio UI with AlphaTranslator_Styled Appearance ---
# (Using CSS variables from AlphaTranslator_Styled for colors and fonts)
FLY_PRIMARY_COLOR_HEX = "#4F46E5"
FLY_SECONDARY_COLOR_HEX = "#10B981"
FLY_ACCENT_COLOR_HEX = "#D97706"
FLY_TEXT_COLOR_HEX = "#1F2937"
FLY_SUBTLE_TEXT_HEX = "#6B7280"
FLY_LIGHT_BACKGROUND_HEX = "#F9FAFB"
FLY_WHITE_HEX = "#FFFFFF"
FLY_BORDER_COLOR_HEX = "#D1D5DB"
FLY_INPUT_BG_HEX_SIMPLE = "#F3F4F6"
FLY_PANEL_BG_SIMPLE = "#E0F2FE"

app_theme_outer_styled = gr.themes.Base( # New theme object name to avoid conflict if you had `app_theme_outer`
    font=[gr.themes.GoogleFont("Inter"), "system-ui", "sans-serif"],
).set(
    body_background_fill=FLY_LIGHT_BACKGROUND_HEX,
)

# CSS from AlphaTranslator_Styled, adapted slightly for your component names/IDs if needed
# Your original component IDs are like "use_file_cb_alpha_v3", "file_uploader_alpha_main_v3", etc.
# The CSS below uses general selectors but can be made more specific if those IDs are kept.
applied_css_for_alphatts = f"""
@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;600;700;800&display=swap');
/* Poppins and Inter are from AlphaTranslator_Styled, Vazirmatn from your AlphaTTS_Original theme */
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700;800&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');
:root {{
    --fly-primary: {FLY_PRIMARY_COLOR_HEX}; --fly-secondary: {FLY_SECONDARY_COLOR_HEX};
    --fly-accent: {FLY_ACCENT_COLOR_HEX}; --fly-text-primary: {FLY_TEXT_COLOR_HEX};
    --fly-text-secondary: {FLY_SUBTLE_TEXT_HEX}; --fly-bg-light: {FLY_LIGHT_BACKGROUND_HEX};
    --fly-bg-white: {FLY_WHITE_HEX}; --fly-border-color: {FLY_BORDER_COLOR_HEX};
    --fly-input-bg-simple: {FLY_INPUT_BG_HEX_SIMPLE}; --fly-panel-bg-simple: {FLY_PANEL_BG_SIMPLE};
    --font-global: 'Vazirmatn', 'Inter', 'Poppins', system-ui, sans-serif; /* Vazirmatn prioritized */
    --font-english: 'Poppins', 'Inter', system-ui, sans-serif; 
    --radius-sm: 0.375rem; --radius-md: 0.5rem; --radius-lg: 0.75rem; --radius-xl: 1rem; --radius-full: 9999px;
    --shadow-xl: 0 20px 25px -5px rgba(0,0,0,0.1),0 8px 10px -6px rgba(0,0,0,0.1);
    --fly-primary-rgb: 79,70,229; --fly-accent-rgb: 217,119,6;
    
    /* Variables from your original AlphaTTS CSS if they were different and needed */
    /* For example, if your original had --app-button-bg for the blue button */
    --app-button-bg-original: #2979FF; /* Blue from your original AlphaTTS button */
    --shadow-button-original: 0 4px 10px -2px rgba(41,121,255,0.5);
    --radius-input-original: 8px;
    --app-border-color-original: #E0E0E0;
    --app-input-bg-original: #F7F7F7;
}}
body {{
    font-family:var(--font-global); direction:rtl; background-color:var(--fly-bg-light);
    color:var(--fly-text-primary); line-height:1.7; font-size:16px;
}}
.gradio-container {{ /* Overall container styling from AlphaTranslator_Styled */
    max-width:100% !important; width:100% !important; min-height:100vh;
    margin:0 auto !important; padding:0 !important; border-radius:0 !important;
    box-shadow:none !important; background:linear-gradient(170deg, #E0F2FE 0%, #F3E8FF 100%);
    display:flex; flex-direction:column;
}}
/* Header styling from AlphaTranslator_Styled */
.app-header-alphatts {{ /* Changed class name slightly to avoid conflict if both apps run */
    text-align:center; padding:2.5rem 1rem; margin:0;
    background:linear-gradient(135deg,var(--fly-primary) 0%,var(--fly-secondary) 100%);
    color:var(--fly-bg-white); border-bottom-left-radius:var(--radius-xl);
    border-bottom-right-radius:var(--radius-xl); box-shadow:var(--shadow-lg);
    position:relative; overflow:hidden;
}}
.app-header-alphatts::before {{ /* Decorative element from AlphaTranslator_Styled */
    content:''; position:absolute; top:-50px; right:-50px; width:150px; height:150px;
    background:rgba(255,255,255,0.1); border-radius:var(--radius-full);
    opacity:0.5; transform:rotate(45deg);
}}
.app-header-alphatts h1 {{ /* h1 from AlphaTranslator_Styled */
    font-size:2.25em !important; font-weight:800 !important; margin:0 0 0.5rem 0;
    font-family:var(--font-english); letter-spacing:-0.5px; text-shadow:0 2px 4px rgba(0,0,0,0.1);
}}
.app-header-alphatts p {{ /* p from AlphaTranslator_Styled */
    font-size:1em !important; margin-top:0.25rem; font-weight:400;
    color:rgba(255,255,255,0.85) !important;
}}

/* Main content panel styling from AlphaTranslator_Styled */
.main-content-area-alphatts {{ /* Changed class name slightly */
    flex-grow:1; padding:0.75rem; width:100%; margin:0 auto; box-sizing:border-box;
}}
.content-panel-alphatts {{ /* Changed class name slightly */
    background-color:var(--fly-bg-white); padding:1rem; border-radius:var(--radius-xl);
    box-shadow:var(--shadow-xl); margin-top:-2rem; position:relative; z-index:10;
    margin-bottom:2rem; width:100%; box-sizing:border-box;
}}

/* Styling for YOUR UI elements, applying AlphaTranslator_Styled aesthetics */
/* Inputs (Textbox, Dropdown, File) */
.content-panel-alphatts .gr-input > label + div > textarea,
.content-panel-alphatts .gr-dropdown > label + div > div > input,
.content-panel-alphatts .gr-dropdown > label + div > div > select,
.content-panel-alphatts .gr-textbox > label + div > textarea, 
.content-panel-alphatts .gr-file > label + div /* For file input styling */
{{
    border-radius:var(--radius-input-original) !important; /* Your original radius */
    border:1.5px solid var(--fly-border-color) !important; /* Border from AlphaTranslator */
    font-size:0.95em !important; background-color:var(--fly-input-bg-simple) !important; /* BG from AlphaTranslator */
    padding:10px 12px !important; color:var(--fly-text-primary) !important;
}}
.content-panel-alphatts .gr-input > label + div > textarea:focus,
.content-panel-alphatts .gr-dropdown > label + div > div > input:focus,
.content-panel-alphatts .gr-dropdown > label + div > div > select:focus,
.content-panel-alphatts .gr-textbox > label + div > textarea:focus,
.content-panel-alphatts .gr-file > label + div:focus-within
{{
    border-color:var(--fly-primary) !important; /* Focus border from AlphaTranslator */
    box-shadow:0 0 0 3px rgba(var(--fly-primary-rgb),0.12) !important;
    background-color:var(--fly-bg-white) !important;
}}
.content-panel-alphatts .gr-file > label + div {{ text-align:center; border-style: dashed !important; }}

/* Button: Using --fly-accent for consistency with AlphaTranslator's primary action color */
.content-panel-alphatts .gr-button[elem_id="generate_button_alpha_v3"], /* Your button ID */
.content-panel-alphatts button[variant="primary"] /* General primary button */
{{
    background:var(--fly-accent) !important; /* Orange accent from AlphaTranslator */
    margin-top:1.5rem !important; padding:12px 20px !important; /* Adjusted padding */
    transition:all 0.25s ease-in-out !important; color:white !important; font-weight:600 !important;
    border-radius:var(--radius-input-original) !important; /* Your original radius */ border:none !important;
    box-shadow:0 3px 8px -1px rgba(var(--fly-accent-rgb),0.3) !important;
    width:100% !important; font-size:1.05em !important; /* Your original font size */
    display:flex; align-items:center; justify-content:center;
}}
.content-panel-alphatts .gr-button[elem_id="generate_button_alpha_v3"]:hover,
.content-panel-alphatts button[variant="primary"]:hover
{{
    background:#B45309 !important; /* Darker orange */ transform:translateY(-1px) !important;
    box-shadow:0 5px 10px -1px rgba(var(--fly-accent-rgb),0.4) !important;
}}

/* Labels (using AlphaTranslator_Styled general label style) */
.content-panel-alphatts label > span.label-text 
{{
    font-weight:500 !important; color:#4B5563 !important; 
    font-size:0.88em !important; margin-bottom:6px !important; display:inline-block;
}}
/* Your original specific label styling with icons (if you want to keep them) */
/* You would need to ensure your Gradio labels have the correct `for` attribute linking to input `elem_id`
   or use JavaScript to add these pseudo-elements if Gradio doesn't directly support `for` on labels.
   For simplicity, I'm omitting the ::before icon styles unless you confirm they are essential
   and your Gradio setup can support them easily. The general label style above will apply.
*/

/* Temperature description (from your original AlphaTTS CSS) */
.content-panel-alphatts .temp_description_class_alpha_v3 {{ 
    font-size: 0.85em; color: #777; margin-top: -0.4rem; margin-bottom: 1rem; 
}}

/* Audio Player (general styling, can be targeted by ID if set) */
.content-panel-alphatts .gr-audio audio, /* General audio player */
.content-panel-alphatts #output_audio_player_alpha_v3 audio /* Your specific ID */
{{ 
    width: 100%; border-radius: var(--radius-input-original); margin-top:0.8rem; 
}}

/* Examples (using AlphaTranslator_Styled examples button style) */
.content-panel-alphatts div[label*="نمونه‌های کاربردی"] .gr-button.gr-button-tool, /* Targetting by label */
.content-panel-alphatts div[label*="نمونه‌های کاربردی"] .gr-sample-button 
{{
    background-color:#E0E7FF !important; color:var(--fly-primary) !important;
    border-radius:6px !important; font-size:0.78em !important; padding:4px 8px !important;
}}
.content-panel-alphatts .custom-hr {{height:1px;background-color:var(--fly-border-color);margin:1.5rem 0;border:none;}}

/* Footer styling from AlphaTranslator_Styled */
.app-footer-alphatts {{ /* Changed class name slightly */
    text-align:center;font-size:0.85em;color:var(--fly-text-secondary);margin-top:2.5rem;
    padding:1rem 0;background-color:rgba(255,255,255,0.3);backdrop-filter:blur(5px);
    border-top:1px solid var(--fly-border-color);
}}
footer {{display:none !important;}} /* Hides default Gradio footer */


/* Responsive adjustments from AlphaTranslator_Styled */
@media (min-width:640px) {{
    .main-content-area-alphatts {{padding:1.5rem;max-width:700px;}} 
    .content-panel-alphatts {{padding:1.5rem;}} 
    .app-header-alphatts h1 {{font-size:2.5em !important;}} 
    .app-header-alphatts p {{font-size:1.05em !important;}} 
}}
@media (min-width:768px) {{
    .main-content-area-alphatts {{max-width:780px;}} 
    .content-panel-alphatts {{padding:2rem;}}
    .content-panel-alphatts .gr-button[elem_id="generate_button_alpha_v3"],
    .content-panel-alphatts button[variant="primary"]
    {{
        width:auto !important; align-self:flex-start;
    }}
    .app-header-alphatts h1 {{font-size:2.75em !important;}} 
    .app-header-alphatts p {{font-size:1.1em !important;}}
}}
"""

# Using your original Gradio Blocks structure
# The theme `gr.themes.Base(font=[gr.themes.GoogleFont("Vazirmatn")])` is from your original.
# We are applying `app_theme_outer_styled` for the body background and `applied_css_for_alphatts` for specifics.
with gr.Blocks(theme=app_theme_outer_styled, css=applied_css_for_alphatts, title=f"آلفا TTS ({FIXED_MODEL_NAME.split('-')[1]})") as demo:
    # Applying the header from AlphaTranslator_Styled structure
    gr.HTML(f"""
        <div class='app-header-alphatts'>
            <h1>🚀 Alpha TTS</h1>
            <p>جادوی تبدیل متن به صدا در دستان شما (Gemini {FIXED_MODEL_NAME.split('-')[1]})</p>
        </div>
    """)

    # Applying the main content panel structure from AlphaTranslator_Styled
    with gr.Column(elem_classes=["main-content-area-alphatts"]):
        with gr.Column(elem_classes=["content-panel-alphatts"]): # Your original AlphaTTS used Column, let's keep it simple
                                                               # AlphaTranslator used Group, but Column is fine.

            # Your original UI layout from AlphaTTS_Original
            # Note: `elem_id`s are from your original AlphaTTS code.
            # CSS selectors have been updated to try and match these or use general selectors.
            
            # Warning if GEMINI_API_KEY is not set
            if not os.environ.get("GEMINI_API_KEY"):
                missing_key_msg = (
                    "⚠️ هشدار: متغیر محیطی GEMINI_API_KEY تنظیم نشده است. "
                    "قابلیت تبدیل متن به گفتار احتمالاً کار نخواهد کرد. "
                    "لطفاً این متغیر را در بخش Secrets این Space تنظیم کنید."
                )
                gr.Markdown(f"<div class='api-warning-message'>{missing_key_msg}</div>")

            use_file_input_cb = gr.Checkbox(label="📄 استفاده از فایل متنی (.txt)", value=False, elem_id="use_file_cb_alpha_v3")
            
            uploaded_file_input = gr.File(
                label=" ", 
                file_types=['.txt'], 
                visible=False,
                elem_id="file_uploader_alpha_main_v3"
            ) 
            
            text_to_speak_tb = gr.Textbox(
                label="متن فارسی برای تبدیل", 
                placeholder="مثال: سلام، فردا هوا چطور است؟", 
                lines=5,
                value="", 
                visible=True,
                elem_id="text_input_main_alpha_v3"
            )
            
            # Your original change function for checkbox
            use_file_input_cb.change(
                fn=lambda x: (gr.update(visible=x, label=" " if x else "متن فارسی برای تبدیل"), gr.update(visible=not x)),
                inputs=use_file_input_cb,
                outputs=[uploaded_file_input, text_to_speak_tb]
            )

            speech_prompt_tb = gr.Textbox(
                label="سبک گفتار (اختیاری)",
                placeholder="مثال: با لحنی شاد و پرانرژی", 
                value="با لحنی دوستانه و رسا صحبت کن.",
                lines=2, elem_id="speech_prompt_alpha_v3"
            )
            
            speaker_voice_dd = gr.Dropdown(
                SPEAKER_VOICES, label="انتخاب گوینده و لهجه", value="Charon", elem_id="speaker_voice_alpha_v3"
            )
            
            temperature_slider = gr.Slider(
                minimum=0.1, maximum=1.5, step=0.05, value=0.9, label="میزان خلاقیت صدا",
                elem_id="temperature_slider_alpha_v3"
            )
            gr.Markdown("<p class='temp_description_class_alpha_v3'>مقادیر بالاتر = تنوع بیشتر، مقادیر پایین‌تر = یکنواختی بیشتر.</p>")

            generate_button = gr.Button("🚀 تولید و پخش صدا", elem_id="generate_button_alpha_v3") # Removed variant="primary" to let CSS handle it via elem_id
            
            output_audio = gr.Audio(label=" ", type="filepath", elem_id="output_audio_player_alpha_v3")
        
            # Your original Examples section
            # Applying a custom HR from AlphaTranslator_Styled
            gr.HTML("<hr class='custom-hr'>")
            gr.Markdown(
                "<h3 style='text-align:center; font-weight:500; color:var(--fly-text-secondary); margin-top:1.5rem; margin-bottom:1rem;'>نمونه‌های کاربردی</h3>",
                # elem_id="examples_section_title_v3" # elem_id from your original
            )
            gr.Examples(
                examples=[
                    [False, None, "سلام بر شما، امیدوارم روز خوبی داشته باشید.", "با لحنی گرم و صمیمی.", "Zephyr", 0.85],
                    [False, None, "این یک آزمایش برای بررسی کیفیت صدای تولید شده توسط هوش مصنوعی آلفا است.", "با صدایی طبیعی و روان.", "Charon", 0.9],
                ],
                inputs=[use_file_input_cb, uploaded_file_input, text_to_speak_tb, speech_prompt_tb, speaker_voice_dd, temperature_slider],
                outputs=[output_audio], # Original AlphaTTS only output to audio
                fn=gradio_tts_interface,
                cache_examples=os.getenv("GRADIO_CACHE_EXAMPLES", "False").lower() == "true" # From AlphaTranslator
            )

    # Footer from AlphaTranslator_Styled
    gr.Markdown(f"<p class='app-footer-alphatts'>Alpha TTS © 2024 - Model: {FIXED_MODEL_NAME}</p>")


    # --- Event Handlers (from YOUR AlphaTTS_Original) ---
    if generate_button is not None:
         generate_button.click(
            fn=gradio_tts_interface,
            inputs=[use_file_input_cb, uploaded_file_input, text_to_speak_tb, speech_prompt_tb, speaker_voice_dd, temperature_slider],
            outputs=[output_audio] # Original AlphaTTS only output to audio
        )
    else:
        logging.error("دکمه تولید صدا (generate_button_alpha_v3) به درستی مقداردهی اولیه نشده است.")
# --- END: Gradio UI ---

if __name__ == "__main__":
    # Removed auto-restart thread to keep it closer to your original AlphaTTS.
    # If you need it, you can re-add the auto_restart_service function and thread start.

    # Check if PYDUB is available at launch
    if not PYDUB_AVAILABLE:
        logging.warning("Pydub (for audio merging) not found. Please install with `pip install pydub`. Merging will be disabled if multiple audio chunks are generated.")

    demo.launch(
        server_name="0.0.0.0",
        server_port=int(os.getenv("PORT", 7860)),
        debug=os.environ.get("GRADIO_DEBUG", "False").lower() == "true",
        show_error=True
    )