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() |