""" app.py – Gradio TTS Space (Arabic only) يدعم: Piper · Supertonic-3 · Chatterbox Multilingual """ import sys print("=== app.py starting ===", flush=True) # ── import check مبكر عشان نشوف أي مشكلة قبل Gradio ────────────────────── _import_errors = [] try: import onnxruntime print(f"[ok] onnxruntime {onnxruntime.__version__}", flush=True) except Exception as e: _import_errors.append(f"onnxruntime: {e}") try: from piper import PiperVoice print("[ok] piper", flush=True) except Exception as e: _import_errors.append(f"piper: {e}") try: from supertonic import TTS as _ST print("[ok] supertonic", flush=True) except Exception as e: _import_errors.append(f"supertonic: {e}") try: from engines import generate_audio, MODELS_CONFIG print("[ok] engines", flush=True) except Exception as e: _import_errors.append(f"engines: {e}") print(f"[FATAL] engines import failed: {e}", flush=True) sys.exit(1) if _import_errors: print("[WARNING] بعض الـ imports فشلت:", flush=True) for err in _import_errors: print(f" - {err}", flush=True) # ────────────────────────────────────────────────────────────────────────── import os import wave import tempfile import gradio as gr MODEL_DESCRIPTIONS = { "Piper – ar (Arabic)": "🎙️ Piper VITS · ONNX CPU · عربي فصحى (أردني)", "Supertonic-3 – M1 (Arabic)": "⚡ Supertonic-3 · صوت ذكر · عربي", "Supertonic-3 – F1 (Arabic)": "⚡ Supertonic-3 · صوت أنثى · عربي", "Chatterbox Multilingual – ar": "🔊 Chatterbox ML · ONNX · عربي", } MODEL_NAMES = list(MODELS_CONFIG.keys()) def update_desc(model_name): return MODEL_DESCRIPTIONS.get(model_name, "") def run_tts(model_name, text): if not model_name: return None, "❌ اختر موديل أولاً" if not text or not text.strip(): return None, "❌ اكتب نصاً أولاً" try: tmp = tempfile.NamedTemporaryFile(suffix=".wav", delete=False) out_path = tmp.name tmp.close() print(f"[run_tts] model={model_name!r} text={text[:60]!r}", flush=True) generate_audio(model_name, text, out_path) exists = os.path.exists(out_path) size = os.path.getsize(out_path) if exists else -1 print(f"[run_tts] exists={exists} size={size}", flush=True) if not exists or size <= 44: return None, f"❌ الملف فارغ أو غير موجود (size={size})" # فحص WAV (Piper بيعمل WAV، Chatterbox بيعمل WAV عبر soundfile) try: with wave.open(out_path, "r") as wf: frames = wf.getnframes() rate = wf.getframerate() print(f"[run_tts] WAV frames={frames} rate={rate} dur={frames/rate:.2f}s", flush=True) if frames == 0: return None, "❌ WAV header موجود بس مفيش audio — راجع terminal" except Exception: # soundfile بيكتب WAV بشكل مختلف أحياناً، متقلقش لو wave.open فشل pass return out_path, f"✅ تم التوليد ({size // 1024} KB)" except Exception as e: import traceback print(f"[run_tts] ERROR:\n{traceback.format_exc()}", flush=True) return None, f"❌ خطأ: {e}" # ── Gradio UI ────────────────────────────────────────────────────────────── with gr.Blocks( title="🎙️ TTS عربي – Piper · Supertonic-3 · Chatterbox", theme=gr.themes.Soft(primary_hue="indigo"), ) as demo: gr.Markdown(""" # 🎙️ مقارنة نماذج TTS العربية **Piper · Supertonic-3 · Chatterbox Multilingual** كل النماذج تعمل على CPU عبر ONNX Runtime. أول تشغيل لكل نموذج يحمّله من HuggingFace تلقائياً. """) with gr.Row(): with gr.Column(scale=1): model_dropdown = gr.Dropdown( choices=MODEL_NAMES, value=MODEL_NAMES[0], label="🤖 اختر النموذج", ) model_desc = gr.Markdown( value=MODEL_DESCRIPTIONS.get(MODEL_NAMES[0], ""), ) text_input = gr.Textbox( label="📝 النص العربي", placeholder="اكتب النص هنا …", lines=5, rtl=True, ) generate_btn = gr.Button("🔊 توليد الصوت", variant="primary") with gr.Column(scale=1): audio_out = gr.Audio( label="🎵 الصوت المولّد", type="filepath", show_download_button=True, interactive=False, ) status_out = gr.Markdown(value="") model_dropdown.change( fn=update_desc, inputs=[model_dropdown], outputs=[model_desc], ) generate_btn.click( fn=run_tts, inputs=[model_dropdown, text_input], outputs=[audio_out, status_out], ) gr.Markdown(""" --- ### 📦 ملاحظات - **Piper**: يحتاج `espeak-ng` مثبّتاً على النظام - **Supertonic-3**: ~400MB عند أول تحميل - **Chatterbox ML**: ~2GB عند أول تحميل """) if __name__ == "__main__": demo.launch()