Reoo88 commited on
Commit
0010fb7
·
verified ·
1 Parent(s): 950b002

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +204 -10
app.py CHANGED
@@ -2,14 +2,26 @@ import os
2
  import time
3
  import subprocess
4
  import threading
 
 
5
  import psutil
 
 
 
 
6
 
 
 
 
7
  TIKTOK_USERNAME = "@osei_iraq"
8
- STREAM_KEY = "p3dq-5cex-bc1k-vet7-bsyx"
9
  FFMPEG_LOG = "/tmp/ffmpeg.log"
10
  MONITOR_LOG = "/tmp/monitor.log"
11
  LOCK_FILE = "/tmp/app_monitor_core.lock"
 
 
12
 
 
13
  TIKTOK_COOKIES = os.getenv("TIKTOK_COOKIES", "")
14
  TIKTOK_COOKIES_ADULT = os.getenv("TIKTOK_COOKIES_ADULT", "")
15
 
@@ -20,6 +32,9 @@ def log(msg):
20
  with open(MONITOR_LOG, "a", encoding="utf-8") as f:
21
  f.write(line + "\n")
22
 
 
 
 
23
  def save_cookies():
24
  if TIKTOK_COOKIES:
25
  with open("/tmp/tiktok_cookies.txt", "w") as f:
@@ -30,6 +45,9 @@ def save_cookies():
30
 
31
  save_cookies()
32
 
 
 
 
33
  def kill_process_tree(pid):
34
  try:
35
  parent = psutil.Process(pid)
@@ -76,7 +94,7 @@ def get_tiktok_stream_url():
76
  def start_stream(stream_key, stream_url):
77
  if not stream_key or not stream_url:
78
  return None
79
- log("🎬 بدء البث (فيديو نسخ + صوت احترافي مع إصلاح التزامن)...")
80
  rtmp_url = f"rtmp://a.rtmp.youtube.com/live2/{stream_key}"
81
 
82
  try:
@@ -92,9 +110,7 @@ def start_stream(stream_key, stream_url):
92
  "-fflags", "+genpts",
93
  "-flags", "+global_header",
94
  "-i", stream_url,
95
-
96
  "-c:v", "copy",
97
-
98
  "-c:a", "aac",
99
  "-b:a", "192k",
100
  "-ar", "44100",
@@ -102,11 +118,9 @@ def start_stream(stream_key, stream_url):
102
  "-cutoff", "18000",
103
  "-af", "highpass=f=60,lowpass=f=17000,aresample=async=1:first_pts=0",
104
  "-async", "1",
105
-
106
  "-start_at_zero",
107
  "-copyts",
108
  "-vsync", "2",
109
-
110
  "-bsf:v", "h264_mp4toannexb",
111
  "-reconnect", "1",
112
  "-reconnect_at_eof", "1",
@@ -122,6 +136,150 @@ def start_stream(stream_key, stream_url):
122
  log(f"✅ ffmpeg PID: {proc.pid}")
123
  return proc
124
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  def monitor_loop():
126
  try:
127
  subprocess.run(["killall", "-9", "ffmpeg", "yt-dlp", "streamlink"], stderr=subprocess.DEVNULL)
@@ -141,13 +299,19 @@ def monitor_loop():
141
  with open(LOCK_FILE, "w") as f:
142
  f.write(str(os.getpid()))
143
 
 
 
 
 
144
  is_streaming = False
145
  ffmpeg_proc = None
146
  was_streaming = False
147
  consecutive_fails = 0
148
  ffmpeg_restart_count = 0
 
 
149
 
150
- log("🚀 محرك النقل الشفاف (بدون واجهة) بدأ")
151
 
152
  while True:
153
  try:
@@ -163,25 +327,51 @@ def monitor_loop():
163
  log("📋 آخر سجلات ffmpeg:\n" + "".join(f.readlines()[-10:]))
164
  ffmpeg_restart_count += 1
165
  if ffmpeg_restart_count >= 10:
166
- log("💀 10 محاولات فاشلة – توقف لمدة 5 دقائق")
167
  kill_process_tree(ffmpeg_proc.pid)
168
  ffmpeg_proc = None
169
  is_streaming = False
170
  was_streaming = False
 
 
 
 
171
  time.sleep(300)
172
  ffmpeg_restart_count = 0
173
  continue
174
  time.sleep(10)
175
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  log("🟢 بدء بث جديد...")
177
  if ffmpeg_proc:
178
  kill_process_tree(ffmpeg_proc.pid)
179
- ffmpeg_proc = start_stream(STREAM_KEY, stream_url)
180
  if ffmpeg_proc:
181
  is_streaming = True
182
  was_streaming = True
183
  consecutive_fails = 0
184
  ffmpeg_restart_count = 0
 
 
 
 
 
 
 
185
  else:
186
  consecutive_fails += 1
187
  if was_streaming and consecutive_fails > 5:
@@ -191,6 +381,10 @@ def monitor_loop():
191
  ffmpeg_proc = None
192
  is_streaming = False
193
  was_streaming = False
 
 
 
 
194
  consecutive_fails = 0
195
 
196
  time.sleep(30)
@@ -200,7 +394,7 @@ def monitor_loop():
200
  time.sleep(30)
201
 
202
  if __name__ == "__main__":
203
- log("✅ بدء تشغيل المحرك بدون واجهة")
204
  t = threading.Thread(target=monitor_loop, daemon=True)
205
  t.start()
206
  while True:
 
2
  import time
3
  import subprocess
4
  import threading
5
+ import json
6
+ import pickle
7
  import psutil
8
+ from google.oauth2.credentials import Credentials
9
+ from google.auth.transport.requests import Request
10
+ from googleapiclient.discovery import build
11
+ from googleapiclient.errors import HttpError
12
 
13
+ # ------------------------------------------------------------
14
+ # الإعدادات الأساسية
15
+ # ------------------------------------------------------------
16
  TIKTOK_USERNAME = "@osei_iraq"
17
+ CUSTOM_STREAM_KEY = os.getenv("CUSTOM_STREAM_KEY", "p3dq-5cex-bc1k-vet7-bsyx") # مفتاح احتياطي
18
  FFMPEG_LOG = "/tmp/ffmpeg.log"
19
  MONITOR_LOG = "/tmp/monitor.log"
20
  LOCK_FILE = "/tmp/app_monitor_core.lock"
21
+ BROADCAST_FILE = "/tmp/current_broadcast.json"
22
+ TOKEN_FILE = "/tmp/yt_token.pkl"
23
 
24
+ # الكوكيز من المتغيرات البيئية
25
  TIKTOK_COOKIES = os.getenv("TIKTOK_COOKIES", "")
26
  TIKTOK_COOKIES_ADULT = os.getenv("TIKTOK_COOKIES_ADULT", "")
27
 
 
32
  with open(MONITOR_LOG, "a", encoding="utf-8") as f:
33
  f.write(line + "\n")
34
 
35
+ # ------------------------------------------------------------
36
+ # إعداد الكوكيز
37
+ # ------------------------------------------------------------
38
  def save_cookies():
39
  if TIKTOK_COOKIES:
40
  with open("/tmp/tiktok_cookies.txt", "w") as f:
 
45
 
46
  save_cookies()
47
 
48
+ # ------------------------------------------------------------
49
+ # أدوات مساعدة
50
+ # ------------------------------------------------------------
51
  def kill_process_tree(pid):
52
  try:
53
  parent = psutil.Process(pid)
 
94
  def start_stream(stream_key, stream_url):
95
  if not stream_key or not stream_url:
96
  return None
97
+ log("🎬 بدء البث (فيديو نسخ + صوت احترافي)...")
98
  rtmp_url = f"rtmp://a.rtmp.youtube.com/live2/{stream_key}"
99
 
100
  try:
 
110
  "-fflags", "+genpts",
111
  "-flags", "+global_header",
112
  "-i", stream_url,
 
113
  "-c:v", "copy",
 
114
  "-c:a", "aac",
115
  "-b:a", "192k",
116
  "-ar", "44100",
 
118
  "-cutoff", "18000",
119
  "-af", "highpass=f=60,lowpass=f=17000,aresample=async=1:first_pts=0",
120
  "-async", "1",
 
121
  "-start_at_zero",
122
  "-copyts",
123
  "-vsync", "2",
 
124
  "-bsf:v", "h264_mp4toannexb",
125
  "-reconnect", "1",
126
  "-reconnect_at_eof", "1",
 
136
  log(f"✅ ffmpeg PID: {proc.pid}")
137
  return proc
138
 
139
+ # ------------------------------------------------------------
140
+ # YouTube API دوال (مأخوذة من الكود المرجعي ومعدلة)
141
+ # ------------------------------------------------------------
142
+ def build_youtube_client():
143
+ """إنشاء عميل YouTube API من التوكن المحفوظ أو من JSON"""
144
+ try:
145
+ # محاولة تحميل توكن موجود
146
+ if os.path.exists(TOKEN_FILE):
147
+ with open(TOKEN_FILE, "rb") as f:
148
+ creds = pickle.load(f)
149
+ if creds and creds.valid:
150
+ return build('youtube', 'v3', credentials=creds)
151
+ if creds and creds.expired and creds.refresh_token:
152
+ creds.refresh(Request())
153
+ with open(TOKEN_FILE, "wb") as f:
154
+ pickle.dump(creds, f)
155
+ return build('youtube', 'v3', credentials=creds)
156
+
157
+ # إنشاء من JSON الموجود في المتغير البيئي
158
+ creds_json = os.getenv("YT_CREDENTIALS_JSON")
159
+ if not creds_json:
160
+ log("❌ YT_CREDENTIALS_JSON غير موجود")
161
+ return None
162
+
163
+ data = json.loads(creds_json)
164
+ creds = Credentials(
165
+ token=data.get("token"),
166
+ refresh_token=data.get("refresh_token"),
167
+ token_uri=data.get("token_uri"),
168
+ client_id=data.get("client_id"),
169
+ client_secret=data.get("client_secret"),
170
+ scopes=data.get("scopes", ["https://www.googleapis.com/auth/youtube.force-ssl"])
171
+ )
172
+ if creds.expired and creds.refresh_token:
173
+ creds.refresh(Request())
174
+ with open(TOKEN_FILE, "wb") as f:
175
+ pickle.dump(creds, f)
176
+ return build('youtube', 'v3', credentials=creds)
177
+ except Exception as e:
178
+ log(f"❌ فشل بناء عميل YouTube: {e}")
179
+ return None
180
+
181
+ def create_live_stream(yt):
182
+ """إنشاء liveStream وإرجاع stream_id و stream_key"""
183
+ try:
184
+ stream_insert = yt.liveStreams().insert(
185
+ part="snippet,cdn,contentDetails",
186
+ body={
187
+ "snippet": {"title": "TikTok Stream Key"},
188
+ "cdn": {
189
+ "ingestionType": "rtmp",
190
+ "resolution": "1080p",
191
+ "frameRate": "30fps"
192
+ },
193
+ "contentDetails": {"isReusable": True}
194
+ }
195
+ ).execute()
196
+ stream_id = stream_insert["id"]
197
+ stream_key = stream_insert["cdn"]["ingestionInfo"]["streamName"]
198
+ log(f"✅ liveStream جاهز: {stream_id} | key: ...{stream_key[-4:]}")
199
+ return stream_id, stream_key
200
+ except Exception as e:
201
+ log(f"❌ فشل إنشاء liveStream: {e}")
202
+ return None, None
203
+
204
+ def bind_stream_to_broadcast(yt, broadcast_id, stream_id):
205
+ try:
206
+ yt.liveBroadcasts().bind(id=broadcast_id, part="id", streamId=stream_id).execute()
207
+ log(f"✅ تم ربط البث {broadcast_id} مع {stream_id}")
208
+ return True
209
+ except Exception as e:
210
+ log(f"❌ فشل ربط البث: {e}")
211
+ return False
212
+
213
+ def finalize_all_broadcasts():
214
+ """إنهاء جميع البثوث النشطة لبداية نظيفة"""
215
+ try:
216
+ yt = build_youtube_client()
217
+ if not yt:
218
+ return
219
+ broadcasts = yt.liveBroadcasts().list(mine=True, part="id,status").execute()
220
+ for item in broadcasts.get("items", []):
221
+ bid = item["id"]
222
+ status = item.get("status", {}).get("lifeCycleStatus", "")
223
+ if status in ["ready", "created", "live", "testing"]:
224
+ try:
225
+ yt.liveBroadcasts().transition(broadcastStatus="complete", id=bid, part="status").execute()
226
+ log(f"🧹 أنهيت البث: {bid} (كان {status})")
227
+ except Exception as e:
228
+ log(f"⚠️ تعذر إنهاء البث {bid}: {e}")
229
+ except Exception as e:
230
+ log(f"❌ خطأ في التنظيف: {e}")
231
+
232
+ def create_new_broadcast():
233
+ """إنشاء بث جديد كامل وإرجاع (stream_key, broadcast_id)"""
234
+ try:
235
+ yt = build_youtube_client()
236
+ if not yt:
237
+ return None, None
238
+
239
+ now = time.strftime("%Y-%m-%d %H:%M:%S")
240
+ title = f"بث تيك توك مباشر | {now}"
241
+ insert_body = {
242
+ "snippet": {"title": title},
243
+ "status": {"privacyStatus": "public"},
244
+ "contentDetails": {"enableAutoStart": True, "enableAutoStop": True, "latencyPreference": "normal"}
245
+ }
246
+ new_broadcast = yt.liveBroadcasts().insert(part="snippet,status,contentDetails", body=insert_body).execute()
247
+ bid = new_broadcast["id"]
248
+ log(f"✅ بث جديد: {bid} | {title}")
249
+
250
+ stream_id, stream_key = create_live_stream(yt)
251
+ if not stream_key:
252
+ return None, None
253
+ if not bind_stream_to_broadcast(yt, bid, stream_id):
254
+ return None, None
255
+
256
+ # حفظ معلومات البث
257
+ with open(BROADCAST_FILE, "w") as f:
258
+ json.dump({"broadcast_id": bid, "stream_key": stream_key}, f)
259
+
260
+ return stream_key, bid
261
+ except Exception as e:
262
+ log(f"❌ فشل إنشاء بث جديد: {e}")
263
+ return None, None
264
+
265
+ def finalize_current_broadcast(broadcast_id):
266
+ """إنهاء بث محدد"""
267
+ if not broadcast_id:
268
+ return
269
+ try:
270
+ yt = build_youtube_client()
271
+ if not yt:
272
+ return
273
+ yt.liveBroadcasts().transition(broadcastStatus="complete", id=broadcast_id, part="status").execute()
274
+ log(f"✅ تم إنهاء البث {broadcast_id}")
275
+ except Exception as e:
276
+ log(f"⚠️ خطأ في إنهاء البث {broadcast_id}: {e}")
277
+ if os.path.exists(BROADCAST_FILE):
278
+ os.remove(BROADCAST_FILE)
279
+
280
+ # ------------------------------------------------------------
281
+ # المحرك الرئيسي (معدل لاستخدام API مع احتياطي المفتاح الثابت)
282
+ # ------------------------------------------------------------
283
  def monitor_loop():
284
  try:
285
  subprocess.run(["killall", "-9", "ffmpeg", "yt-dlp", "streamlink"], stderr=subprocess.DEVNULL)
 
299
  with open(LOCK_FILE, "w") as f:
300
  f.write(str(os.getpid()))
301
 
302
+ # تنظيف البثوث القديمة عبر API
303
+ log("🧹 تنظيف البثوث السابقة...")
304
+ finalize_all_broadcasts()
305
+
306
  is_streaming = False
307
  ffmpeg_proc = None
308
  was_streaming = False
309
  consecutive_fails = 0
310
  ffmpeg_restart_count = 0
311
+ current_stream_key = None
312
+ current_broadcast_id = None
313
 
314
+ log("🚀 محرك النقل الشفاف (YouTube API + احتياطي) بدأ")
315
 
316
  while True:
317
  try:
 
327
  log("📋 آخر سجلات ffmpeg:\n" + "".join(f.readlines()[-10:]))
328
  ffmpeg_restart_count += 1
329
  if ffmpeg_restart_count >= 10:
330
+ log("�� 10 محاولات فاشلة – توقف 5 دقائق")
331
  kill_process_tree(ffmpeg_proc.pid)
332
  ffmpeg_proc = None
333
  is_streaming = False
334
  was_streaming = False
335
+ if current_broadcast_id:
336
+ finalize_current_broadcast(current_broadcast_id)
337
+ current_broadcast_id = None
338
+ current_stream_key = None
339
  time.sleep(300)
340
  ffmpeg_restart_count = 0
341
  continue
342
  time.sleep(10)
343
 
344
+ # الحصول على مفتاح بث جديد (API أولاً، ثم احتياطي)
345
+ stream_key = None
346
+ bid = None
347
+ # محاولة إنشاء بث عبر API
348
+ stream_key, bid = create_new_broadcast()
349
+ if not stream_key:
350
+ log("⚠️ فشل إنشاء بث بالـAPI، استخدام المفتاح الاحتياطي...")
351
+ stream_key = CUSTOM_STREAM_KEY
352
+ if not stream_key:
353
+ log("❌ لا يوجد مفتاح احتياطي")
354
+ time.sleep(30)
355
+ continue
356
+ else:
357
+ current_broadcast_id = bid
358
+
359
  log("🟢 بدء بث جديد...")
360
  if ffmpeg_proc:
361
  kill_process_tree(ffmpeg_proc.pid)
362
+ ffmpeg_proc = start_stream(stream_key, stream_url)
363
  if ffmpeg_proc:
364
  is_streaming = True
365
  was_streaming = True
366
  consecutive_fails = 0
367
  ffmpeg_restart_count = 0
368
+ current_stream_key = stream_key
369
+ log(f"🔑 المفتاح الحالي: ...{stream_key[-4:]}")
370
+ else:
371
+ if current_broadcast_id:
372
+ finalize_current_broadcast(current_broadcast_id)
373
+ current_broadcast_id = None
374
+ current_stream_key = None
375
  else:
376
  consecutive_fails += 1
377
  if was_streaming and consecutive_fails > 5:
 
381
  ffmpeg_proc = None
382
  is_streaming = False
383
  was_streaming = False
384
+ if current_broadcast_id:
385
+ finalize_current_broadcast(current_broadcast_id)
386
+ current_broadcast_id = None
387
+ current_stream_key = None
388
  consecutive_fails = 0
389
 
390
  time.sleep(30)
 
394
  time.sleep(30)
395
 
396
  if __name__ == "__main__":
397
+ log("✅ بدء تشغيل المحرك مع YouTube API")
398
  t = threading.Thread(target=monitor_loop, daemon=True)
399
  t.start()
400
  while True: