import os import uuid import asyncio import gradio as gr import edge_tts from huggingface_hub import hf_hub_download # ── إعداد التوكن من متغير البيئة ───────────────────────────── HF_TOKEN = os.environ.get("HF_TOKEN", "") if not HF_TOKEN: raise ValueError("يجب تعيين متغير البيئة HF_TOKEN") # استخدام المجلد المؤقت للنظام (أكثر استقراراً في الاستضافات) VOICES_DIR = "/tmp/voices" os.makedirs(VOICES_DIR, exist_ok=True) DATASET_REPO = "asim00/voices" # ── قائمة الأصوات ───────────────────────────────────────── EDGE_VOICES = [ "ar-EG-SalmaNeural", "ar-EG-ShakirNeural", "ar-SA-HamedNeural", "ar-SA-ZariyahNeural", "ar-JO-SanaNeural", "ar-JO-IsmaeelNeural", "ar-AE-FatimaNeural", "ar-AE-HamdanNeural", "ar-QA-MoazNeural", "ar-KW-NouraNeural", "ar-MA-JamalNeural", "ar-MA-MounaNeural", "ar-TN-HediNeural", "ar-TN-ReemNeural", "ar-LB-LaylaNeural", "ar-LB-RamiNeural", "ar-DZ-KarimNeural", "ar-DZ-AminaNeural" ] # ══════════════════════════════════════════════════════════════════ # توليد الصوت (التخاطب الآمن مع خادم التزامن في Gradio) # ══════════════════════════════════════════════════════════════════ async def _execute_tts(text: str, voice: str, filepath: str): """الدالة الداخلية لإتمام الاتصال واستلام الملف""" communicate = edge_tts.Communicate(text.strip(), voice) await communicate.save(filepath) def generate_edge_audio(text: str, voice: str) -> str: """توليد الصوت بطريقة مرنة تتفادى مشكلة No audio was received""" filename = f"edge_{uuid.uuid4().hex}.mp3" filepath = os.path.join(VOICES_DIR, filename) try: # محاولة جلب حلقة الأحداث الجارية أو إنشاء واحدة جديدة بأمان try: loop = asyncio.get_event_loop() except RuntimeError: loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) if loop.is_running(): # الحل الأضمن داخل Gradio: تشغيل الكوروتين في خيط آمن والانتظار future = asyncio.run_coroutine_threadsafe(_execute_tts(text, voice, filepath), loop) future.result(timeout=60) # مهلة دقيقة واحدة كحد أقصى للاتصال else: loop.run_until_complete(_execute_tts(text, voice, filepath)) # التأكد من أن الملف تم كتابته وحجمه ليس صفراً if os.path.exists(filepath) and os.path.getsize(filepath) > 0: return filepath else: raise RuntimeError("الملف الصوتي الناتج فارغ أو لم يتم إنشاؤه") except Exception as e: raise RuntimeError(f"خطأ أثناء معالجة الاتصال بمحرك مايكروسوفت: {str(e)}") def synth_and_report(text: str, voice: str): if not text.strip(): return None, "⚠️ الرجاء كتابة نص في الصندوق أولاً" try: path = generate_edge_audio(text, voice) return path, "✅ تم توليد الصوت بنجاح (Edge-TTS)" except Exception as e: return None, f"❌ فشل التوليد: {str(e)}" # ── جلب الملف النصي من الخزنة ────────────────────────────── def load_text_file(): try: path = hf_hub_download( repo_id=DATASET_REPO, filename="Final_Arabic_Files", repo_type="dataset", token=HF_TOKEN ) with open(path, "r", encoding="utf-8") as f: content = f.read() return content, "✅ تم تحميل الملف النصي بنجاح" except Exception as e: return "", f"⚠️ تعذر تحميل الملف: {e}" # ══════════════════════════════════════════════════════════════════ # واجهة التفاعل (Gradio 6.0) # ══════════════════════════════════════════════════════════════════ CSS = """ body, .gradio-container { direction: rtl !important; font-family: 'Segoe UI', Tahoma; } """ with gr.Blocks(title="منصة الأستاذ عبود - Microsoft Edge-TTS") as demo: gr.Markdown("# 🎙️ منصة الأستاذ عبود - محرك مايكروسوفت النقي") with gr.Row(): text_box = gr.Textbox( label="✍️ صندوق النص المراد تحويله إلى صوت", lines=10, rtl=True, placeholder="الصق النص العربي هنا..." ) with gr.Row(): voice_dropdown = gr.Dropdown( choices=EDGE_VOICES, label="🎙️ اختر صوت مايكروسوفت", value="ar-JO-IsmaeelNeural" ) with gr.Row(): btn_generate = gr.Button("🚀 إطلاق التوليد الصوتي", variant="primary") btn_load = gr.Button("📂 تحميل الملف النصي من الخزنة", variant="secondary") with gr.Row(): audio_output = gr.Audio(label="الملف الصوتي الناتج", type="filepath") status_output = gr.Textbox(label="تقرير النظام", interactive=False) # ربط الأحداث btn_generate.click( fn=synth_and_report, inputs=[text_box, voice_dropdown], outputs=[audio_output, status_output] ) btn_load.click( fn=load_text_file, inputs=[], outputs=[text_box, status_output] ) if __name__ == "__main__": # تمرير الـ CSS هنا تفادياً لتحذيرات Gradio 6.0 الجديدة demo.queue().launch(css=CSS)