Reoo88 commited on
Commit
d7dadc7
·
verified ·
1 Parent(s): a298505

Update streamlit_app.py

Browse files
Files changed (1) hide show
  1. streamlit_app.py +77 -22
streamlit_app.py CHANGED
@@ -12,7 +12,7 @@ from googleapiclient.discovery import build
12
  from TikTokLive import TikTokLiveClient
13
 
14
  # ══════════════════════════════════════════════════════════════════════════════ #
15
- # الإعدادات
16
  # ══════════════════════════════════════════════════════════════════════════════ #
17
  TIKTOK_USERNAME = "@vimtoo.2"
18
  TOKEN = "/tmp/yt.pkl"
@@ -22,7 +22,7 @@ POLL_INTERVAL = 60
22
  GRACE_PERIOD_SECONDS = 120
23
 
24
  # ══════════════════════════════════════════════════════════════════════════════ #
25
- # دالة تنظيف وتجهيز الكوكيز الحساسة فقط لمنع خطأ 400 Bad Request
26
  # ══════════════════════════════════════════════════════════════════════════════ #
27
  def get_clean_tiktok_cookies():
28
  raw_cookies = os.getenv("TIKTOK_COOKIES")
@@ -56,32 +56,54 @@ def get_clean_tiktok_cookies():
56
 
57
  return raw_cookies[:200] if len(raw_cookies) > 200 else raw_cookies
58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  # ══════════════════════════════════════════════════════════════════════════════ #
60
  # دالة إنشاء البث الحي وتوليد مفتاح البث تلقائياً عبر الـ API
61
  # ══════════════════════════════════════════════════════════════════════════════ #
62
  def create_dynamic_youtube_stream(yt):
63
  try:
 
 
 
64
  current_date = datetime.now().strftime("%d-%m-%Y")
65
 
66
- # 1. إنشاء البث (Broadcast) وتحديد العنوان والوصف الافتراضي
67
  broadcast_body = {
68
  "snippet": {
69
- "title": f"بث مباشر ومستمر | {current_date}",
70
- "description": f"بث تلقائي مؤرشف بتاريخ: {current_date}\n#بث_مباشر #تيكتوك",
71
- "categoryId": "22" # فئة الأشخاص والمدونات العامة
72
  },
73
  "status": {
74
- "privacyStatus": "public", # البث عام للجميع
75
  "selfDeclaredCreativeCommons": False
76
  }
77
  }
78
  broadcast_res = yt.liveBroadcasts().insert(part="snippet,status", body=broadcast_body).execute()
79
  broadcast_id = broadcast_res["id"]
80
 
81
- # 2. إنشاء نقطة تدفق البيانات (Live Stream) لتوليد مفتاح البث تلقائياً
82
  stream_body = {
83
  "snippet": {
84
- "title": f"Stream Key For - {current_date}"
85
  },
86
  "cdn": {
87
  "frameRate": "variable",
@@ -91,32 +113,62 @@ def create_dynamic_youtube_stream(yt):
91
  }
92
  stream_res = yt.liveStreams().insert(part="snippet,cdn", body=stream_body).execute()
93
  stream_id = stream_res["id"]
94
-
95
- # استخراج مفتاح البث السري المولد ديناميكياً
96
  stream_key = stream_res["cdn"]["ingestionInfo"]["streamName"]
97
 
98
  # 3. ربط البث بمفتاح البث المولد
99
  yt.liveBroadcasts().bind(id=broadcast_id, part="id", streamId=stream_id).execute()
100
 
101
- print(f"[API] Created Dynamic Stream Successfully. Key: {stream_key[:5]}...")
102
  return broadcast_id, stream_key
103
  except Exception as e:
104
  print(f"Error in creating dynamic stream: {e}")
105
  return None, None
106
 
107
  # ══════════════════════════════════════════════════════════════════════════════ #
108
- # دالة إنهاء البث الحالي وحذفه من قائمة البثوث النشطة ليتحول فوراً لأرشيف
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  # ══════════════════════════════════════════════════════════════════════════════ #
110
  def close_dynamic_youtube_stream(yt, broadcast_id):
111
  try:
112
  if not broadcast_id: return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  yt.liveBroadcasts().transition(broadcastStatus="complete", id=broadcast_id, part="status").execute()
114
  print(f"[API] Broadcast {broadcast_id} closed and saved to archive.")
115
  except Exception as e:
116
  print(f"Error closing dynamic stream: {e}")
117
 
118
  # ══════════════════════════════════════════════════════════════════════════════ #
119
- # م��رك المراقبة المدمج بنظام المفاتيح الذكية
120
  # ══════════════════════════════════════════════════════════════════════════════ #
121
  def run_monitor():
122
  lock_file = open(LOCK_FILE, 'w')
@@ -150,9 +202,9 @@ def run_monitor():
150
  if is_live:
151
  offline_start_time = None
152
  if not is_streaming:
153
- with open(STATUS, "w") as f: json.dump({"live": False, "msg": "تم رصد بث جديد! جاري إنشاء مفتاح بث ذكي على يوتيوب..."}, f)
154
 
155
- # 🔥 توليد البث والمفتاح ديناميكياً لتجنب التعليق نهائياً
156
  broadcast_id, dynamic_key = create_dynamic_youtube_stream(yt)
157
 
158
  if dynamic_key:
@@ -160,7 +212,7 @@ def run_monitor():
160
  cookie_string = get_clean_tiktok_cookies()
161
  header_option = f'--http-header "Cookie={cookie_string}"' if cookie_string else ""
162
 
163
- # تمرير المفتاح الديناميكي المولد ذكياً إلى الـ ffmpeg
164
  cmd = (
165
  f'streamlink --loglevel error {header_option} "https://www.tiktok.com/{TIKTOK_USERNAME}/live" best --hls-live-edge 3 --stdout | '
166
  f"ffmpeg -hide_banner -loglevel error -thread_queue_size 8192 -i pipe:0 "
@@ -172,8 +224,11 @@ def run_monitor():
172
  process = subprocess.Popen(cmd, shell=True)
173
  is_streaming = True
174
  with open(STATUS, "w") as f: json.dump({"live": True}, f)
 
 
 
175
  else:
176
- with open(STATUS, "w") as f: json.dump({"live": False, "msg": "فشل إنشاء مفتاح البث، جاري المحاولة لاحقاً..."}, f)
177
 
178
  else:
179
  if is_streaming:
@@ -192,16 +247,16 @@ def run_monitor():
192
  except Exception:
193
  time.sleep(POLL_INTERVAL)
194
 
195
- # تشغيل الخيط
196
  if "core_monitor_initialized" not in st.session_state:
197
  threading.Thread(target=run_monitor, name="CoreTikTokMonitor", daemon=True).start()
198
  st.session_state.core_monitor_initialized = True
199
 
200
  # ══════════════════════════════════════════════════════════════════════════════ #
201
- # الواجهة
202
  # ══════════════════════════════════════════════════════════════════════════════ #
203
  st.set_page_config(page_title="AutoStream Pro", page_icon="📡")
204
- st.title("📡 رادار البث الذكي الآلي بالكامل")
205
 
206
  def get_status():
207
  if not os.path.exists(STATUS) or os.path.getsize(STATUS) == 0:
@@ -212,6 +267,6 @@ def get_status():
212
 
213
  s = get_status()
214
  if s.get("live"):
215
- st.success(f"🔴 حساب {TIKTOK_USERNAME} مباشر الآن (نظام المفاتيح الديناميكية الذكية مفعل)")
216
  else:
217
  st.info(s.get("msg", "بانتظار المذيع..."))
 
12
  from TikTokLive import TikTokLiveClient
13
 
14
  # ══════════════════════════════════════════════════════════════════════════════ #
15
+ # الإعدادات العامة
16
  # ══════════════════════════════════════════════════════════════════════════════ #
17
  TIKTOK_USERNAME = "@vimtoo.2"
18
  TOKEN = "/tmp/yt.pkl"
 
22
  GRACE_PERIOD_SECONDS = 120
23
 
24
  # ══════════════════════════════════════════════════════════════════════════════ #
25
+ # دالة تنظيف وتجهيز الكوكيز الحساسة لمنع خطأ 400 Bad Request
26
  # ══════════════════════════════════════════════════════════════════════════════ #
27
  def get_clean_tiktok_cookies():
28
  raw_cookies = os.getenv("TIKTOK_COOKIES")
 
56
 
57
  return raw_cookies[:200] if len(raw_cookies) > 200 else raw_cookies
58
 
59
+ # ══════════════════════════════════════════════════════════════════════════════ #
60
+ # دالة تصفير وإنهاء أي بث ديناميكي سابق معلق في الحساب لضمان نظافة السيرفر
61
+ # ══════════════════════════════════════════════════════════════════════════════ #
62
+ def cleanup_old_broadcasts(yt):
63
+ try:
64
+ res = yt.liveBroadcasts().list(broadcastType="all", mine=True, part="id,status").execute()
65
+ if res.get("items"):
66
+ for item in res["items"]:
67
+ b_id = item["id"]
68
+ life_status = item["status"].get("lifeCycleStatus")
69
+ if life_status in ["live", "testing", "ready", "liveStarting"]:
70
+ try:
71
+ yt.liveBroadcasts().transition(broadcastStatus="complete", id=b_id, part="status").execute()
72
+ print(f"[API Cleanup] Force closed old stuck broadcast: {b_id}")
73
+ except Exception:
74
+ pass
75
+ except Exception as e:
76
+ print(f"[API Cleanup] Error cleaning old broadcasts: {e}")
77
+
78
  # ══════════════════════════════════════════════════════════════════════════════ #
79
  # دالة إنشاء البث الحي وتوليد مفتاح البث تلقائياً عبر الـ API
80
  # ══════════════════════════════════════════════════════════════════════════════ #
81
  def create_dynamic_youtube_stream(yt):
82
  try:
83
+ # تنظيف الحساب أولاً من أي بث عالق قبل البدء بالجديد
84
+ cleanup_old_broadcasts(yt)
85
+
86
  current_date = datetime.now().strftime("%d-%m-%Y")
87
 
88
+ # 1. إنشاء البث (Broadcast) وتحديد العناوين والخصوصية (عام للجميع)
89
  broadcast_body = {
90
  "snippet": {
91
+ "title": f"تغطية مباشرة ومستمر | أحمد عبدالله",
92
+ "description": f"بث تلقائي آلي مؤرشف بتاريخ: {current_date}\n#بث_مباشر #أحمد_عبدالله",
93
+ "categoryId": "22"
94
  },
95
  "status": {
96
+ "privacyStatus": "public",
97
  "selfDeclaredCreativeCommons": False
98
  }
99
  }
100
  broadcast_res = yt.liveBroadcasts().insert(part="snippet,status", body=broadcast_body).execute()
101
  broadcast_id = broadcast_res["id"]
102
 
103
+ # 2. إنشاء نقطة تدفق البيانات وتوليد الـ Stream Key ديناميكياً
104
  stream_body = {
105
  "snippet": {
106
+ "title": f"Automated Key - {current_date}"
107
  },
108
  "cdn": {
109
  "frameRate": "variable",
 
113
  }
114
  stream_res = yt.liveStreams().insert(part="snippet,cdn", body=stream_body).execute()
115
  stream_id = stream_res["id"]
 
 
116
  stream_key = stream_res["cdn"]["ingestionInfo"]["streamName"]
117
 
118
  # 3. ربط البث بمفتاح البث المولد
119
  yt.liveBroadcasts().bind(id=broadcast_id, part="id", streamId=stream_id).execute()
120
 
121
+ print(f"[API] Created Dynamic Stream. Broadcast ID: {broadcast_id}, Key Generated.")
122
  return broadcast_id, stream_key
123
  except Exception as e:
124
  print(f"Error in creating dynamic stream: {e}")
125
  return None, None
126
 
127
  # ══════════════════════════════════════════════════════════════════════════════ #
128
+ # دالة الانتقال بالبث إلى وضع "الحي والمباشر" (تحل مشكلة عدم الظهور)
129
+ # ══════════════════════════════════════════════════════════════════════════════ #
130
+ def transition_to_live_status(yt, broadcast_id):
131
+ # نمنح ffmpeg مهلة 5 ثوانٍ لضخ أولى حزم البيانات ليقبل يوتيوب التحويل
132
+ time.sleep(5)
133
+ for _ in range(3): # محاولة التكرار في حال تأخر معالجة يوتيوب للإشارة
134
+ try:
135
+ yt.liveBroadcasts().transition(broadcastStatus="live", id=broadcast_id, part="status").execute()
136
+ print(f"[API] Broadcast {broadcast_id} is now successfully LIVE on YouTube!")
137
+ break
138
+ except Exception as e:
139
+ print(f"[API] Waiting for stream data to switch live... Retrying. ({e})")
140
+ time.sleep(4)
141
+
142
+ # ══════════════════════════════════════════════════════════════════════════════ #
143
+ # دالة إنهاء البث الحالي وحفظ الأرشيف ملحقاً بالتاريخ في نهايته
144
  # ══════════════════════════════════════════════════════════════════════════════ #
145
  def close_dynamic_youtube_stream(yt, broadcast_id):
146
  try:
147
  if not broadcast_id: return
148
+
149
+ # جلب البيانات الحالية لإضافة التاريخ قبل الإغلاق التام
150
+ try:
151
+ b_data = yt.liveBroadcasts().list(id=broadcast_id, part="snippet").execute()
152
+ if b_data.get("items"):
153
+ snippet = b_data["items"][0]["snippet"]
154
+ current_date = datetime.now().strftime("%d-%m-%Y")
155
+
156
+ # إضافة التاريخ في نهاية السطر المكتوب
157
+ snippet["title"] = f"{snippet.get('title', 'بث مباشر')} - {current_date}"
158
+ snippet["description"] = f"{snippet.get('description', '')}\n\nتم إنهاء الأرشفة بنجاح بتاريخ: {current_date}"
159
+
160
+ yt.liveBroadcasts().update(part="snippet", body={"id": broadcast_id, "snippet": snippet}).execute()
161
+ except Exception:
162
+ pass
163
+
164
+ # تحويل حالة البث إلى مكتمل ليغلق الغرفة تماماً ويحفظ الفيديو بالقناة
165
  yt.liveBroadcasts().transition(broadcastStatus="complete", id=broadcast_id, part="status").execute()
166
  print(f"[API] Broadcast {broadcast_id} closed and saved to archive.")
167
  except Exception as e:
168
  print(f"Error closing dynamic stream: {e}")
169
 
170
  # ══════════════════════════════════════════════════════════════════════════════ #
171
+ # محرك المراقبة المدمج بنظام الجلسات الذكية
172
  # ══════════════════════════════════════════════════════════════════════════════ #
173
  def run_monitor():
174
  lock_file = open(LOCK_FILE, 'w')
 
202
  if is_live:
203
  offline_start_time = None
204
  if not is_streaming:
205
+ with open(STATUS, "w") as f: json.dump({"live": False, "msg": "تم رصد بث جديد! جاري إنشاء غرفة بث وتوليد مفتاح ذكي على يوتيوب..."}, f)
206
 
207
+ # توليد البث والمفتاح ديناميكياً لتجنب التعليق
208
  broadcast_id, dynamic_key = create_dynamic_youtube_stream(yt)
209
 
210
  if dynamic_key:
 
212
  cookie_string = get_clean_tiktok_cookies()
213
  header_option = f'--http-header "Cookie={cookie_string}"' if cookie_string else ""
214
 
215
+ # إطلاق عملية سحب وضخ الإشارة إلى يوتيوب
216
  cmd = (
217
  f'streamlink --loglevel error {header_option} "https://www.tiktok.com/{TIKTOK_USERNAME}/live" best --hls-live-edge 3 --stdout | '
218
  f"ffmpeg -hide_banner -loglevel error -thread_queue_size 8192 -i pipe:0 "
 
224
  process = subprocess.Popen(cmd, shell=True)
225
  is_streaming = True
226
  with open(STATUS, "w") as f: json.dump({"live": True}, f)
227
+
228
+ # تفعيل خيط مستقل لتحديث الحالة لـ LIVE على يوتيوب دون تعطيل عملية الضخ الحالية
229
+ threading.Thread(target=transition_to_live_status, args=(yt, broadcast_id), daemon=True).start()
230
  else:
231
+ with open(STATUS, "w") as f: json.dump({"live": False, "msg": "فشل إنشاء البث، جاري المحاولة لاحقاً..."}, f)
232
 
233
  else:
234
  if is_streaming:
 
247
  except Exception:
248
  time.sleep(POLL_INTERVAL)
249
 
250
+ # تشغيل خيط الرادار والمراقبة
251
  if "core_monitor_initialized" not in st.session_state:
252
  threading.Thread(target=run_monitor, name="CoreTikTokMonitor", daemon=True).start()
253
  st.session_state.core_monitor_initialized = True
254
 
255
  # ══════════════════════════════════════════════════════════════════════════════ #
256
+ # واجهة العرض (Streamlit UI)
257
  # ══════════════════════════════════════════════════════════════════════════════ #
258
  st.set_page_config(page_title="AutoStream Pro", page_icon="📡")
259
+ st.title("📡 رادار البث الآلي الذكي بالكامل")
260
 
261
  def get_status():
262
  if not os.path.exists(STATUS) or os.path.getsize(STATUS) == 0:
 
267
 
268
  s = get_status()
269
  if s.get("live"):
270
+ st.success(f"🔴 حساب {TIKTOK_USERNAME} مباشر الآن على يوتيوب بنجاح (النظام المطور)")
271
  else:
272
  st.info(s.get("msg", "بانتظار المذيع..."))