File size: 10,468 Bytes
399b056
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
166
167
import gradio as gr
import datetime
import json
import re

import pycountry
from curl_cffi import requests
from lxml import html

# --- كلاس TikTokUser (يبقى كما هو) ---
class TikTokUser:
    def __init__(self, data) -> None:
        user = data.get("user", {})
        stats = data.get("stats", {})

        self.username = user.get("uniqueId", "N/A")
        self.name = user.get("nickname", "N/A")
        self.avatar = user.get("avatarMedium", "")
        self.bio = user.get("signature", "لا توجد سيرة ذاتية.")
        self.created = user.get("createTime", None)
        self.verified = user.get("verified", False)
        self.private = user.get("privateAccount", False)
        self.country_code = user.get("region", "N/A")
        self.language_code = user.get("language", "N/A")

        self.followers = stats.get("followerCount", 0)
        self.following = stats.get("followingCount", 0)
        self.hearts = stats.get("heart", 0)
        self.video_count = stats.get("videoCount", 0)
        self.friends = stats.get("friendCount", 0)

        if self.created:
            try:
                self.created = datetime.datetime.fromtimestamp(self.created).strftime('%Y-%m-%d %H:%M:%S')
            except (TypeError, ValueError):
                self.created = "غير معروف"
        else:
            self.created = "غير معروف"

        self.country = "غير معروف"
        if self.country_code != "N/A":
            try:
                self.country = pycountry.countries.get(alpha_2=self.country_code.upper()).name
            except AttributeError:
                self.country = f"رمز ({self.country_code})"

        self.language = "غير معروف"
        if self.language_code != "N/A":
            try:
                self.language = pycountry.languages.get(alpha_2=self.language_code.lower()).name
            except AttributeError:
                self.language = f"رمز ({self.language_code})"

        if bio_link := user.get("bioLink", {}):
            if link_url := bio_link.get("link"):
                self.bio += f"\n\n🔗 رابط السيرة الذاتية: {link_url}"

        self.followers_formatted = f"{self.followers:,}"
        self.following_formatted = f"{self.following:,}"
        self.hearts_formatted = f"{self.hearts:,}"
        self.video_count_formatted = f"{self.video_count:,}"
        self.friends_formatted = f"{self.friends:,}"

# --- دالة get_user_info (تبقى كما هي، مع تعديل بسيط للتعامل مع الأخطاء لـ Gradio) ---
def get_user_info_from_tiktok(username):
    username = re.sub(r"^.*@", "", username).strip()
    if not username:
        return "خطأ: اسم المستخدم لا يمكن أن يكون فارغاً."

    url = f"https://www.tiktok.com/@{username}"
    print(f"محاولة جلب البيانات من: {url}")

    try:
        r = requests.get(url, impersonate="chrome", timeout=15)
        r.raise_for_status()

        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)
        user_info_data = data.get("__DEFAULT_SCOPE__", {})\
                             .get("webapp.user-detail", {})\
                             .get("userInfo")

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

        user = TikTokUser(user_info_data)

        # تنسيق المخرجات لـ Gradio
        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: #f9f9f9; box-shadow: 0 4px 15px rgba(0,0,0,0.1);">
            <div style="display: flex; align-items: center; gap: 20px; margin-bottom: 20px; flex-wrap: wrap; justify-content: flex-end;">
                <img src="{user.avatar if user.avatar else 'https://via.placeholder.com/120/CCCCCC/000000?text=لا+صورة'}" alt="صورة الملف الشخصي" style="width: 120px; height: 120px; border-radius: 50%; object-fit: cover; border: 3px solid #FE2C55; box-shadow: 0 2px 8px rgba(0,0,0,0.15);">
                <div style="text-align: right; flex-grow: 1;">
                    <h2 style="margin: 0; color: #222; font-size: 2em;">
                        {user.name}
                        {"<span style='color: #4CAF50; font-weight: bold; margin-right: 8px;'>✅ (موثق)</span>" if user.verified else ""}
                        {"<span style='color: #FFC107; font-weight: bold; margin-right: 8px;'>🔒 (خاص)</span>" if user.private else ""}
                    </h2>
                    <p style="margin: 5px 0 0 0; color: #555; font-size: 1.1em;">{user.username}</p>
                </div>
            </div>

            <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 15px; margin-top: 20px;">
                <div style="background-color: #fff; padding: 15px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); text-align: center;">
                    <strong style="display: block; font-size: 1.6em; color: #FE2C55; margin-bottom: 5px;">{user.followers_formatted}</strong>
                    <span style="font-size: 0.9em; color: #777;">متابع</span>
                </div>
                <div style="background-color: #fff; padding: 15px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); text-align: center;">
                    <strong style="display: block; font-size: 1.6em; color: #FE2C55; margin-bottom: 5px;">{user.following_formatted}</strong>
                    <span style="font-size: 0.9em; color: #777;">يتابع</span>
                </div>
                <div style="background-color: #fff; padding: 15px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); text-align: center;">
                    <strong style="display: block; font-size: 1.6em; color: #FE2C55; margin-bottom: 5px;">{user.hearts_formatted}</strong>
                    <span style="font-size: 0.9em; color: #777;">إعجاب</span>
                </div>
                <div style="background-color: #fff; padding: 15px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); text-align: center;">
                    <strong style="display: block; font-size: 1.6em; color: #FE2C55; margin-bottom: 5px;">{user.video_count_formatted}</strong>
                    <span style="font-size: 0.9em; color: #777;">فيديو</span>
                </div>
            </div>

            <div style="margin-top: 25px; padding: 15px; background-color: #eef; border-radius: 8px; text-align: right; border-top: 1px solid #e0e0e0;">
                <h3 style="margin-top: 0; color: #444; font-size: 1.3em; border-bottom: 2px solid #FE2C55; padding-bottom: 5px; display: inline-block;">السيرة الذاتية:</h3>
                <p style="margin: 10px 0; line-height: 1.6; color: #444; white-space: pre-wrap; word-wrap: break-word;">{user.bio}</p>
            </div>

            <div style="margin-top: 25px; padding: 15px; background-color: #fdfdfd; border-radius: 8px; text-align: right; border-top: 1px solid #e0e0e0;">
                <h3 style="margin-top: 0; color: #444; font-size: 1.3em; border-bottom: 2px solid #FE2C55; padding-bottom: 5px; display: inline-block;">معلومات إضافية:</h3>
                <p style="margin: 8px 0; line-height: 1.5; color: #555;"><strong>الدولة:</strong> {user.country}</p>
                <p style="margin: 8px 0; line-height: 1.5; color: #555;"><strong>اللغة:</strong> {user.language}</p>
                <p style="margin: 8px 0; line-height: 1.5; color: #555;"><strong>تاريخ الإنشاء:</strong> {user.created}</p>
            </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;">كما أنها قد تتعارض مع شروط خدمة تيك توك، وتؤدي إلى حظر عنوان IP الخاص بك.</span>
                <span style="display: block; margin-top: 5px; font-size: 0.8em; color: #c00;">استخدم على مسؤوليتك الخاصة ولأغراض تعليمية فقط.</span>
            </p>
        </div>
        """
        return output_html

    except Exception as e:
        return f"حدث خطأ: {str(e)}. يرجى المحاولة مرة أخرى باسم مستخدم صحيح وتأكد من أن الحساب عام."

# --- واجهة Gradio ---
iface = gr.Interface(
    fn=get_user_info_from_tiktok, # الدالة التي ستقوم بالعمل
    inputs=gr.Textbox(label="أدخل اسم مستخدم تيك توك (بدون @)", placeholder="مثال: cristiano"), # مدخل نصي
    outputs=gr.HTML(label="معلومات مستخدم تيك توك"), # مخرج HTML لعرض المعلومات المنسقة
    title="جالب معلومات مستخدم تيك توك (تجريبي)",
    description="قم بإدخال اسم مستخدم تيك توك لمحاولة جلب معلوماته. هذه الطريقة تعتمد على الاستخلاص وقد تفشل.",
    theme="soft", # يمكنك تغيير الثيم
    allow_flagging="never" # لا يسمح بوضع علامات على النتائج
)

if __name__ == "__main__":
    iface.launch()
    # local_host=True
    # share=True # لإنشاء رابط مشاركة مؤقت إذا كنت تختبر محلياً