File size: 10,893 Bytes
5ea3020
 
f71cd62
d864c28
f71cd62
 
 
d864c28
f71cd62
 
f31c502
5ea3020
f71cd62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d885d7d
5ea3020
f31c502
f71cd62
5ea3020
 
d864c28
f71cd62
d864c28
f71cd62
 
 
5ea3020
f71cd62
5ea3020
 
f71cd62
d864c28
5ea3020
 
f71cd62
d885d7d
 
 
 
 
5ea3020
f71cd62
5ea3020
f31c502
f71cd62
d864c28
5ea3020
f71cd62
5ea3020
 
 
 
f71cd62
 
 
d864c28
 
f71cd62
d864c28
 
 
 
 
 
 
 
 
 
 
 
 
f71cd62
d864c28
f71cd62
 
 
 
 
d864c28
f71cd62
 
 
 
 
 
 
d864c28
f71cd62
 
d864c28
f71cd62
 
 
 
 
d864c28
f71cd62
 
d864c28
f71cd62
 
d864c28
5ea3020
f71cd62
f31c502
f71cd62
 
5ea3020
 
f71cd62
5ea3020
f71cd62
d864c28
 
5ea3020
f71cd62
5ea3020
f71cd62
 
d864c28
 
 
f71cd62
d864c28
f71cd62
 
 
 
 
d864c28
5ea3020
f71cd62
 
 
5ea3020
f71cd62
5ea3020
d864c28
5ea3020
f71cd62
d885d7d
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
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)