import gradio as gr import json import matplotlib.pyplot as plt # استيراد مكتبة Matplotlib لرسم المخططات import numpy as np # قد نحتاجه لبعض العمليات الحسابية البسيطة import matplotlib.font_manager as fm # استيراد font_manager للتحكم في الخطوط import arabic_reshaper # لاستعادة شكل الحروف العربية from bidi.algorithm import get_display # لضبط اتجاه النص RTL # استيراد الدالة الرئيسية لتقييم قائمة تشغيل YouTube من ملف main.py # تأكد أن ملف main.py موجود في نفس المجلد وأن النماذج (.pkl) متاحة له from main import evaluate_youtube_playlist_individually_same_method2 # ======================================================= # إعداد الخطوط لـ Matplotlib لدعم اللغة العربية # ======================================================= # يجب التأكد من تثبيت خط عربي في بيئة Docker الخاصة بـ Hugging Face Space. # مثال على التثبيت في Dockerfile: RUN apt-get update && apt-get install -y fonts-noto-arabic # سنبحث عن خط Noto Sans Arabic، وإذا لم نجده، سيظل Matplotlib يستخدم الخط الافتراضي font_path = None # البحث عن خط Noto Sans Arabic أو Tajawal (يمكنك تغيير هذا بناءً على الخط الذي تفضله وتثبته) for font in fm.findSystemFonts(fontpaths=None): if 'NotoSansArabic' in font or 'Tajawal' in font or 'Amiri' in font: font_path = font break if font_path: # إضافة الخط العربي إلى قائمة الخطوط الافتراضية لـ Matplotlib plt.rcParams['font.sans-serif'] = [fm.FontProperties(fname=font_path).get_name()] + plt.rcParams['font.sans-serif'] # تعطيل عرض علامة السالب كـ "صندوق" في بعض الخطوط plt.rcParams['axes.unicode_minus'] = False # تعيين عائلة الخط الافتراضية plt.rcParams['font.family'] = 'sans-serif' print(f"تم تعيين الخط العربي لـ Matplotlib: {font_path}") # لغرض التصحيح في السجلات else: print("تحذير: لم يتم العثور على خط عربي مناسب. قد لا يظهر النص العربي بشكل صحيح في المخطط.") gr.Warning("تحذير: لم يتم العثور على خط عربي مناسب. قد لا يظهر النص العربي بشكل صحيح في المخطط.") async def evaluate_playlist_local(youtube_url: str, max_comments_per_video: int = 50, max_workers: int = 3): """ تستدعي دالة تقييم قائمة تشغيل YouTube مباشرة من ملف main.py. وتعيد النتائج النصية مجمعة في صندوق واحد، بالإضافة إلى مخطط Matplotlib يوضح نسب التعليقات. """ error_message = "" # تهيئة المخرجات الافتراضية في حالة وجود خطأ أو عدم وجود رابط default_text_output = "الرجاء إدخال رابط صحيح وتشغيل التقييم." # إنشاء شكل Matplotlib فارغ لتجنب الأخطاء في حالة عدم وجود بيانات للمخطط default_plot = plt.figure() plt.close(default_plot) # إغلاق الشكل الافتراضي لمنع عرضه غير الضروري # التحقق من أن الرابط ليس فارغًا قبل المتابعة if not youtube_url: error_message = "الرجاء إدخال رابط قائمة تشغيل أو فيديو يوتيوب." gr.Warning(error_message) # عرض تحذير للمستخدم في واجهة Gradio return error_message, default_plot # إرجاع رسالة خطأ ومخطط فارغ try: # استدعاء الدالة الرئيسية لمعالجة قائمة التشغيل/الفيديو result = await evaluate_youtube_playlist_individually_same_method2( youtube_url=youtube_url, max_comments_per_video=max_comments_per_video, max_workers=max_workers ) # التحقق مما إذا كانت الدالة قد أعادت خطأ if "error" in result: error_message = f"خطأ في المعالجة: {result['error']}" gr.Error(error_message) # عرض الخطأ كرسالة خطأ حمراء في Gradio return error_message, default_plot # إرجاع رسالة الخطأ ومخطط فارغ # استخراج النتائج المطلوبة من كائن JSON المُعاد overall_quality = result.get("overall_quality", "غير متوفر") composite_quality = result.get("composite_quality", "غير متوفر") composite_score = result.get("composite_score", "غير متوفر") percent_good_videos = result.get("percent_good_videos", "غير متوفر") # التأكد من أن النسب عددية، وإلا سيتم تعيينها إلى 0.0 positive_ratio = result.get("positive_ratio", 0.0) negative_ratio = result.get("negative_ratio", 0.0) # ======================================================= # 1. إعداد النص الموحد ليتم عرضه في صندوق واحد (باستخدام Markdown للتنسيق) # ======================================================= formatted_output_text = f""" ## 📊 نتائج تقييم قائمة التشغيل: - **الجودة العامة:** **{overall_quality}** - **الجودة المركبة:** **{composite_quality}** - **النقاط المركبة:** **{composite_score} / 100** - **نسبة الفيديوهات الجيدة:** **{percent_good_videos}%** - **نسبة التعليقات الإيجابية:** **{positive_ratio}%** - **نسبة التعليقات السلبية:** **{negative_ratio}%** """ # ======================================================= # 2. رسم المخطط باستخدام Matplotlib (مخطط دائري) # ======================================================= # إنشاء شكل ومحاور للمخطط بحجم مناسب fig, ax = plt.subplots(figsize=(6, 4)) # النصوص العربية التي ستظهر على المخطط labels_arabic = ['إيجابية', 'سلبية'] # تطبيق arabic_reshaper و get_display على التسميات العربية # هذا هو الجزء الذي يحل مشكلة قلب النص وتقطيعه reshaped_labels = [get_display(arabic_reshaper.reshape(label)) for label in labels_arabic] sizes = [positive_ratio, negative_ratio] # قيم النسب المئوية colors = ['#4CAF50', '#F44336'] # ألوان الشرائح (أخضر للإيجابي، أحمر للسلبي) explode = (0.1, 0) # "تفجير" الشريحة الإيجابية قليلاً لإبرازها (اختياري) # رسم المخطط الدائري باستخدام التسميات المصححة ax.pie(sizes, explode=explode, labels=reshaped_labels, colors=colors, autopct='%1.1f%%', shadow=True, startangle=90, textprops={'fontsize': 12}) ax.axis('equal') # يضمن أن يكون المخطط دائريًا متناسبًا. # تطبيق arabic_reshaper و get_display على عنوان المخطط reshaped_title = get_display(arabic_reshaper.reshape('توزيع التعليقات')) ax.set_title(reshaped_title, fontsize=14, pad=20) # إغلاق الشكل لمنع Matplotlib من عرضه تلقائيًا في الخلفية، Gradio سيتولى العرض. plt.close(fig) # إرجاع النص المنسق والمخطط كناتجين للدالة return formatted_output_text, fig except Exception as e: # التقاط أي أخطاء غير متوقعة أثناء المعالجة error_message = f"حدث خطأ غير متوقع أثناء المعالجة: {e}" gr.Error(error_message) # عرض رسالة الخطأ للمستخدم return error_message, default_plot # إرجاع رسالة الخطأ ومخطط فارغ في حالة الفشل # بناء واجهة Gradio باستخدام gr.Blocks للتحكم في التخطيط with gr.Blocks(title="تقييم قائمة تشغيل يوتيوب") as demo: # عنوان ووصف الواجهة gr.Markdown("# 📊 تقييم جودة قائمة تشغيل يوتيوب") gr.Markdown("أدخل رابط قائمة تشغيل أو فيديو يوتيوب لتقييم جودتها بناءً على التحليلات والمشاعر.") # تقسيم الواجهة إلى صفين (Row) لأعمدة المدخلات والمخرجات with gr.Row(): # العمود الأول للمدخلات with gr.Column(scale=1): youtube_url_input = gr.Textbox(label="رابط قائمة تشغيل/فيديو يوتيوب", placeholder="الصق رابط يوتيوب هنا...", interactive=True) max_comments_input = gr.Slider(minimum=10, maximum=200, value=50, step=10, label="الحد الأقصى للتعليقات لكل فيديو", interactive=True) max_workers_input = gr.Slider(minimum=1, maximum=10, value=3, step=1, label="الحد الأقصى للعمال المتوازيين", interactive=True) submit_button = gr.Button("🚀 تقييم قائمة التشغيل", variant="primary") # زر الإرسال الرئيسي # العمود الثاني للمخرجات (بجعل scale=2 ليكون أكبر) with gr.Column(scale=2): # صندوق نصي واحد لعرض جميع النتائج النصية المنسقة output_text_box = gr.Markdown(label="النتائج النهائية للتقييم") # عنصر Gradio لعرض مخطط Matplotlib output_plot = gr.Plot(label="توزيع التعليقات (إيجابية مقابل سلبية)") # ربط زر الإرسال بالدالة evaluate_playlist_local # المدخلات هي عناصر واجهة المستخدم التي توفر البيانات للدالة # المخرجات هي عناصر واجهة المستخدم التي ستعرض النتائج من الدالة submit_button.click( evaluate_playlist_local, inputs=[youtube_url_input, max_comments_input, max_workers_input], outputs=[output_text_box, output_plot] ) # تشغيل واجهة Gradio demo.launch(debug=True)