File size: 10,605 Bytes
7c771ea
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import gradio as gr
import datetime
import json
import re

import pycountry # قد لا تكون ضرورية هنا ما لم نكن نعرض بيانات مستخدم من الفيديو
from curl_cffi import requests
from lxml import html

# دالة لجلب تفاصيل الفيديو من رابط تيك توك
def get_tiktok_video_details(video_url):
    # استخدام نمط regex لاستخلاص معرف الفيديو من الرابط (إذا كان موجوداً)
    # تيك توك لديها روابط مختلفة، مثل:
    # https://www.tiktok.com/@username/video/VIDEO_ID
    # https://www.tiktok.com/t/VIDEO_ID/
    # https://vm.tiktok.com/ZM.../
    match_video_id = re.search(r'(?:video/|/v/|/t/|vm\.tiktok\.com/ZM)([0-9A-Za-z_-]+)', video_url)
    
    if not match_video_id:
        return "خطأ: رابط الفيديو غير صالح. يرجى إدخال رابط فيديو تيك توك صحيح."

    video_id = match_video_id.group(1)
    
    # بناء رابط صفحة الفيديو (قد لا يكون URL الأساسي هو الأفضل دائماً، أحياناً vm.tiktok.com يعمل بشكل أفضل)
    # لكن سنستخدم الصيغة الأكثر شيوعاً التي تعرض معلومات المستخدم أيضاً.
    # تيك توك تعيد التوجيه من الروابط القصيرة إلى الروابط الطويلة تلقائياً.
    final_url = f"https://www.tiktok.com/foryou?is_from_webapp=1&sender_device=pc&web_id=7372990422119335456&vid={video_id}"
    # يمكننا تجربة أيضاً: f"https://www.tiktok.com/embed/v2/{video_id}" أو f"https://www.tiktok.com/share/video/{video_id}"

    print(f"محاولة جلب بيانات الفيديو من: {final_url}")

    try:
        r = requests.get(final_url, impersonate="chrome", timeout=15)
        r.raise_for_status() # ترفع استثناء لأخطاء HTTP

        tree = html.fromstring(r.text, parser=html.HTMLParser(encoding="utf8"))
        
        # البحث عن السكربت الذي يحتوي على البيانات الأولية
        # يختلف المسار لبيانات الفيديو عن بيانات المستخدم
        script_tag = tree.xpath('//script[@id="__UNIVERSAL_DATA_FOR_REHYDRATION__"]')
        
        if not script_tag:
            return ("خطأ: لم يتم العثور على وسم البيانات في الصفحة. "
                    "قد يكون هيكل الصفحة قد تغير، أو تم حظر الطلب.")

        data = json.loads(script_tag[0].text)
        
        # المسار المتوقع لبيانات الفيديو في JSON
        # هذا المسار يمكن أن يتغير مع تحديثات تيك توك
        video_data_raw = data.get("__DEFAULT_SCOPE__", {})\
                             .get("webapp.video-detail", {})\
                             .get("itemInfo", {})\
                             .get("itemStruct", {})

        if not video_data_raw:
            return (f"خطأ: لم يتم العثور على تفاصيل للفيديو: '{video_id}'. "
                    "تأكد من صحة الرابط وأن الفيديو متاح للعامة.")

        # استخلاص البيانات من الكائن المستخرج
        video_info = {
            "title": video_data_raw.get("desc", "لا يتوفر عنوان"),
            "author_username": video_data_raw.get("author", {}).get("uniqueId", "غير معروف"),
            "author_nickname": video_data_raw.get("author", {}).get("nickname", "غير معروف"),
            "create_time": datetime.datetime.fromtimestamp(video_data_raw.get("createTime", 0)).strftime('%Y-%m-%d %H:%M:%S') if video_data_raw.get("createTime") else "غير معروف",
            "play_count": f"{video_data_raw.get('stats', {}).get('playCount', 0):,}",
            "digg_count": f"{video_data_raw.get('stats', {}).get('diggCount', 0):,}", # الإعجابات
            "comment_count": f"{video_data_raw.get('stats', {}).get('commentCount', 0):,}",
            "share_count": f"{video_data_raw.get('stats', {}).get('shareCount', 0):,}",
            "save_count": f"{video_data_raw.get('stats', {}).get('collectCount', 0):,}", # حفظ الفيديو
            "cover_url": video_data_raw.get("video", {}).get("cover", {}).get("url_list", [""])[0],
            "video_url": f"https://www.tiktok.com/@{video_data_raw.get('author', {}).get('uniqueId', 'username')}/video/{video_id}"
        }

        # بناء مخرج HTML المنسق
        output_html = f"""
        <div style="direction: rtl; text-align: right; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; padding: 20px; border-radius: 8px; background-color: #ffffff; box-shadow: 0 4px 15px rgba(0,0,0,0.1);">
            <h2 style="margin: 0; color: #FE2C55; font-size: 1.8em; border-bottom: 2px solid #FE2C55; padding-bottom: 5px;">{video_info['title']}</h2>
            <p style="margin: 10px 0; color: #555; font-size: 1.1em;"><strong>المستخدم:</strong> <a href="https://www.tiktok.com/@{video_info['author_username']}" target="_blank" style="color: #007bff; text-decoration: none;">@{video_info['author_username']} ({video_info['author_nickname']})</a></p>
            <p style="margin: 5px 0; color: #555; font-size: 1.1em;"><strong>تاريخ الإنشاء:</strong> {video_info['create_time']}</p>
            
            <div style="display: flex; justify-content: center; margin-top: 20px;">
                <img src="{video_info['cover_url']}" alt="صورة غلاف الفيديو" style="max-width: 100%; height: auto; border-radius: 8px; object-fit: cover; box-shadow: 0 4px 10px rgba(0,0,0,0.15);">
            </div>

            <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); gap: 15px; margin-top: 25px; text-align: center;">
                <div style="background-color: #f0f2f5; padding: 10px; border-radius: 8px;">
                    <strong style="display: block; font-size: 1.4em; color: #FE2C55;">{video_info['play_count']}</strong>
                    <span style="font-size: 0.9em; color: #777;">مشاهدة</span>
                </div>
                <div style="background-color: #f0f2f5; padding: 10px; border-radius: 8px;">
                    <strong style="display: block; font-size: 1.4em; color: #28a745;">{video_info['digg_count']}</strong>
                    <span style="font-size: 0.9em; color: #777;">إعجاب</span>
                </div>
                <div style="background-color: #f0f2f5; padding: 10px; border-radius: 8px;">
                    <strong style="display: block; font-size: 1.4em; color: #6c757d;">{video_info['comment_count']}</strong>
                    <span style="font-size: 0.9em; color: #777;">تعليق</span>
                </div>
                <div style="background-color: #f0f2f5; padding: 10px; border-radius: 8px;">
                    <strong style="display: block; font-size: 1.4em; color: #007bff;">{video_info['share_count']}</strong>
                    <span style="font-size: 0.9em; color: #777;">مشاركة</span>
                </div>
                <div style="background-color: #f0f2f5; padding: 10px; border-radius: 8px;">
                    <strong style="display: block; font-size: 1.4em; color: #ffc107;">{video_info['save_count']}</strong>
                    <span style="font-size: 0.9em; color: #777;">حفظ</span>
                </div>
            </div>

            <div style="margin-top: 25px; text-align: center;">
                <a href="{video_info['video_url']}" target="_blank" style="background-color: #FE2C55; color: white; padding: 10px 20px; border-radius: 5px; text-decoration: none; font-weight: bold; display: inline-block;">مشاهدة الفيديو على تيك توك</a>
            </div>

            <p style="font-size: 0.9em; color: #a00; margin-top: 30px; padding-top: 15px; border-top: 1px dashed #f00; text-align: center; font-weight: bold;">
                ⚠️ ملاحظة هامة: البيانات مستخلصة من صفحة تيك توك.
                <span style="display: block; margin-top: 5px; font-size: 0.8em; color: #c00;">هذه الطريقة غير رسمية وغير مستقرة وقد تتوقف عن العمل في أي وقت.</span>
                <span style="display: block; margin-top: 5px; font-size: 0.8em; color: #c00;">استخدم على مسؤوليتك الخاصة ولأغراض تعليمية فقط. لا يمكن لهذا التطبيق كتابة تعليقات.</span>
            </p>
        </div>
        """
        return gr.update(value=output_html, visible=True), gr.update(value="", visible=False)

    except requests.exceptions.RequestException as e:
        error_msg = f"خطأ في الاتصال بتيك توك: {e}. قد يكون عنوان IP محظوراً."
        return gr.update(value="", visible=False), gr.update(value=error_msg, visible=True)
    except (KeyError, ValueError, IndexError, json.JSONDecodeError) as e:
        error_msg = f"خطأ في تحليل بيانات الفيديو: {e}. قد يكون هيكل صفحة تيك توك قد تغير."
        return gr.update(value="", visible=False), gr.update(value=error_msg, visible=True)
    except Exception as e:
        error_msg = f"حدث خطأ غير متوقع: {e}. يرجى المحاولة مرة أخرى."
        return gr.update(value="", visible=False), gr.update(value=error_msg, visible=True)

# --- واجهة Gradio ---
with gr.Blocks(theme="soft", title="جالب معلومات فيديو تيك توك") as demo:
    gr.Markdown("# جالب معلومات فيديو تيك توك")
    gr.Markdown("أدخل رابط فيديو تيك توك (مثال: `https://www.tiktok.com/@username/video/VIDEO_ID`) للحصول على تفاصيله.")

    with gr.Row():
        video_url_input = gr.Textbox(label="رابط فيديو تيك توك", placeholder="مثال: https://www.tiktok.com/@cristiano/video/7300762635957095713", scale=3)
        submit_button = gr.Button("جلب التفاصيل", scale=1)
    
    # مكونات الإخراج
    output_html_display = gr.HTML(label="تفاصيل الفيديو", visible=False)
    error_message_display = gr.Markdown(visible=False)

    # تعريف التفاعل
    submit_button.click(
        fn=get_tiktok_video_details,
        inputs=video_url_input,
        outputs=[output_html_display, error_message_display]
    )

if __name__ == "__main__":
    demo.launch()