Mroo9 commited on
Commit
2f5cee1
·
verified ·
1 Parent(s): 2edb533

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +52 -68
app.py CHANGED
@@ -5,26 +5,22 @@ import threading
5
  import json
6
  import pickle
7
  import psutil
8
- from datetime import datetime, timezone
9
  from google.oauth2.credentials import Credentials
10
  from google.auth.transport.requests import Request
11
  from googleapiclient.discovery import build
12
  from flask import Flask
13
 
14
  # ------------------------------------------------------------
15
- # الإعدادات الأساسية (تتغير ديناميكياً لتفادي تداخل المشاريع)
16
  # ------------------------------------------------------------
17
  TIKTOK_USERNAME = "@a3dam_111"
18
-
19
- # استخراج اسم نقي للملفات لمنع التداخل والاصطدام بين السكربتات الشغالة معاً
20
- USER_TAG = TIKTOK_USERNAME.replace("@", "").strip()
21
-
22
  CUSTOM_STREAM_KEY = os.getenv("CUSTOM_STREAM_KEY", "p3dq-5cex-bc1k-vet7-bsyx")
23
- FFMPEG_LOG = f"/tmp/ffmpeg_{USER_TAG}.log"
24
- MONITOR_LOG = f"/tmp/monitor_{USER_TAG}.log"
25
- LOCK_FILE = f"/tmp/app_monitor_core_{USER_TAG}.lock"
26
- TOKEN_FILE = f"/tmp/yt_token_{USER_TAG}.pkl"
27
- BROADCAST_FILE = f"/tmp/current_broadcast_{USER_TAG}.json"
28
 
29
  TIKTOK_COOKIES = os.getenv("TIKTOK_COOKIES", "")
30
  TIKTOK_COOKIES_ADULT = os.getenv("TIKTOK_COOKIES_ADULT", "")
@@ -37,14 +33,14 @@ def log(msg):
37
  f.write(line + "\n")
38
 
39
  # ------------------------------------------------------------
40
- # حفظ الكوكيز باسم معزول
41
  # ------------------------------------------------------------
42
  def save_cookies():
43
  if TIKTOK_COOKIES:
44
- with open(f"/tmp/tiktok_cookies_{USER_TAG}.txt", "w") as f:
45
  f.write(TIKTOK_COOKIES)
46
  if TIKTOK_COOKIES_ADULT:
47
- with open(f"/tmp/tiktok_cookies_adult_{USER_TAG}.txt", "w") as f:
48
  f.write(TIKTOK_COOKIES_ADULT)
49
 
50
  save_cookies()
@@ -62,24 +58,24 @@ def kill_process_tree(pid):
62
  pass
63
 
64
  def get_tiktok_stream_url():
65
- cookies_adult = f"/tmp/tiktok_cookies_adult_{USER_TAG}.txt"
66
- cookies_normal = f"/tmp/tiktok_cookies_{USER_TAG}.txt"
67
-
68
  try:
69
  url = f"https://www.tiktok.com/{TIKTOK_USERNAME}/live"
70
- cmd = ["streamlink", "--stream-url", url, "best", "--retry-streams", "5", "--retry-open", "3"]
71
- if os.path.exists(cookies_adult): cmd.extend(["--cookies", cookies_adult])
72
- elif os.path.exists(cookies_normal): cmd.extend(["--cookies", cookies_normal])
 
 
73
  result = subprocess.run(cmd, capture_output=True, text=True, timeout=25)
74
  if result.returncode == 0 and result.stdout.strip():
75
  return result.stdout.strip().split("\n")[0]
76
  except: pass
77
-
78
  try:
79
  url = f"https://www.tiktok.com/{TIKTOK_USERNAME}/live"
80
  cmd = ["yt-dlp", "--no-warnings", "--no-check-certificates", "--no-live-from-start", "--no-cache-dir", "--rm-cache-dir", "-f", "best", "--get-url", url]
81
- if os.path.exists(cookies_adult): cmd.extend(["--cookies", cookies_adult])
82
- elif os.path.exists(cookies_normal): cmd.extend(["--cookies", cookies_normal])
 
 
83
  result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
84
  if result.returncode == 0 and result.stdout.strip():
85
  return result.stdout.strip().split("\n")[0]
@@ -88,34 +84,36 @@ def get_tiktok_stream_url():
88
 
89
  def start_stream(stream_key, stream_url):
90
  if not stream_key or not stream_url: return None
91
- log("🎬 بدء البث (تزامن كامل الصوت والصورة + تحويل H.264)...")
92
  rtmp_url = f"rtmp://a.rtmp.youtube.com/live2/{stream_key}"
93
 
94
- # تم حذف سطر killall العام لمنع إغلاق السكربتات الأخرى الشغالة معاً
95
-
96
  cmd = [
97
  "nice", "-n", "19", "ffmpeg", "-hide_banner", "-loglevel", "warning",
98
  "-thread_queue_size", "2048",
99
  "-user_agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
100
  "-headers", "Referer: https://www.tiktok.com/\r\n",
101
 
102
- # --- علاج مشكلة عدم تزامن الصوت والصورة عند تقطيع إنترنت المذيع ---
103
- "-use_wallclock_as_timestamps", "1",
104
- "-fflags", "+genpts+igndts+discardcorrupt",
105
- "-flags", "+global_header", "-i", stream_url,
 
 
 
 
 
 
 
106
 
107
- # تثبيت الـ Framerate على 30 لسد الفجوات الزمنية عند سقوط الإطارات
108
- "-c:v", "libx264", "-preset", "ultrafast", "-b:v", "2500k", "-maxrate", "2500k", "-bufsize", "5000k", "-pix_fmt", "yuv420p", "-g", "60", "-r", "30",
109
- "-c:a", "aac", "-b:a", "192k", "-ar", "44100", "-profile:a", "aac_low", "-af", "aresample=async=1000:min_hard_comp=0.100000:first_pts=0,highpass=f=50,lowpass=f=15000,dynaudnorm=f=150:g=15:m=25:p=0.95",
110
- "-reconnect", "1", "-reconnect_at_eof", "1", "-reconnect_delay_max", "60", "-rtmp_live", "live", "-max_muxing_queue_size", "2048", "-f", "flv", rtmp_url
111
  ]
112
  with open(FFMPEG_LOG, "w") as stderr_file:
113
  proc = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=stderr_file, preexec_fn=os.setsid)
114
- log(f"✅ ffmpeg PID: {proc.pid} للمستخدم {TIKTOK_USERNAME}")
115
  return proc
116
 
117
  # ------------------------------------------------------------
118
- # YouTube API (الفحص والتعرف الذكي عبر البصمة الوصفية)
119
  # ------------------------------------------------------------
120
  def build_youtube_client():
121
  try:
@@ -142,33 +140,37 @@ def create_new_broadcast():
142
  yt = build_youtube_client()
143
  if not yt: return None, None
144
 
145
- now = datetime.now(timezone.utc)
 
146
  date_str = now.strftime('%d-%m-%Y %H:%M')
147
- title = f"🔥 بث تيك توك أعدام المباشر | {date_str} 🚀"
148
 
149
- # حقن بصمة الحساب السرية داخل الوصف لمنع السكربتات الأخرى من حذفه
150
  description = (
151
- "أهلاً بكم في البث المباشر المخصص لـ دكتور إعدام.\n"
152
- f"معرف الحساب المسؤول: [{TIKTOK_USERNAME}]\n"
153
  "استمتعوا معنا بأجواء البث وتفاعلوا في التعليقات!\n\n"
154
  "🔔 لا تنسوا الاشتراك في القناة وتفعيل زر التنبيهات ليصلكم كل جديد فور انطلاق البث.\n"
155
  "شكراً لدعمكم المتواصل!\n\n"
156
- "#دكتور_إعدام #بث_مباشر #تيك_توك #TikTokLive #العراق #بث #مباشر #a3dam_111"
157
  )
158
 
159
  insert_body = {
160
- "snippet": {"title": title, "description": description, "scheduledStartTime": now.isoformat()},
 
 
 
 
161
  "status": {"privacyStatus": "public"},
162
  "contentDetails": {"enableAutoStart": True, "enableAutoStop": False, "latencyPreference": "normal"}
163
  }
164
 
165
  new_broadcast = yt.liveBroadcasts().insert(part="snippet,status,contentDetails", body=insert_body).execute()
166
  bid = new_broadcast["id"]
167
- log(f"✅ بث جديد تم إنشاؤه للـ ID: {bid}")
168
 
 
169
  stream_insert = yt.liveStreams().insert(
170
  part="snippet,cdn,contentDetails",
171
- body={"snippet": {"title": f"TikTok Stream Key {USER_TAG}"}, "cdn": {"ingestionType": "rtmp", "resolution": "1080p", "frameRate": "30fps"}, "contentDetails": {"isReusable": True}}
172
  ).execute()
173
  stream_id = stream_insert["id"]
174
  stream_key = stream_insert["cdn"]["ingestionInfo"]["streamName"]
@@ -186,18 +188,13 @@ def finalize_all_broadcasts():
186
  try:
187
  yt = build_youtube_client()
188
  if not yt: return
189
- # قراءة الـ snippet لفحص الوصف والتحقق من بصمة المشروع
190
- broadcasts = yt.liveBroadcasts().list(mine=True, part="id,status,snippet", maxResults=50).execute()
191
  for item in broadcasts.get("items", []):
192
  bid = item["id"]
193
  status = item.get("status", {}).get("lifeCycleStatus", "")
194
- description = item.get("snippet", {}).get("description", "")
195
-
196
- # نغلق البث فقط وفقط إذا كان يحمل بصمة هذا المستخدم لعدم تدمير البث الآخر
197
- if status in ["ready", "created", "live", "testing"] and f"[{TIKTOK_USERNAME}]" in description:
198
  try:
199
  yt.liveBroadcasts().transition(broadcastStatus="complete", id=bid, part="status").execute()
200
- log(f"🧹 تم تنظيف وإغلاق بث قديم معلق يخص الحساب {TIKTOK_USERNAME}")
201
  except: pass
202
  except: pass
203
 
@@ -211,21 +208,12 @@ def finalize_current_broadcast(broadcast_id):
211
  if os.path.exists(BROADCAST_FILE): os.remove(BROADCAST_FILE)
212
 
213
  # ------------------------------------------------------------
214
- # المحرك الرئيسي الآمن والمعزول كلياً
215
  # ------------------------------------------------------------
216
  def monitor_loop():
217
- # حذفنا أمر killall الشامل الذي كان يسبب انهيار السكربت الأول عند نهوض الثاني
218
- if os.path.exists(LOCK_FILE):
219
- try:
220
- with open(LOCK_FILE) as f:
221
- os.kill(int(f.read().strip()), 0)
222
- log(f"🔒 محرك الحساب {TIKTOK_USERNAME} يعمل بالفعل بشكل مستقر.")
223
- return
224
- except:
225
- os.remove(LOCK_FILE)
226
-
227
  with open(LOCK_FILE, "w") as f: f.write(str(os.getpid()))
228
-
229
  finalize_all_broadcasts()
230
  ffmpeg_proc = None
231
  was_streaming = False
@@ -233,8 +221,6 @@ def monitor_loop():
233
  current_stream_key = None
234
  current_broadcast_id = None
235
 
236
- log(f"🚀 تم إطلاق محرك المراقبة الخاص بـ {TIKTOK_USERNAME} بأمان وبصمة منفصلة.")
237
-
238
  while True:
239
  try:
240
  stream_url = get_tiktok_stream_url()
@@ -273,6 +259,4 @@ def index(): return "OK"
273
  if __name__ == "__main__":
274
  t = threading.Thread(target=monitor_loop, daemon=True)
275
  t.start()
276
-
277
- # تنبيه: إذا قمت بتشغيل السكربت الثاني على نفس السيرفر، يرجى تغيير الـ port هنا إلى 781 مثلاً
278
  app.run(host="0.0.0.0", port=7860, debug=False)
 
5
  import json
6
  import pickle
7
  import psutil
8
+ from datetime import datetime, timezone, timedelta
9
  from google.oauth2.credentials import Credentials
10
  from google.auth.transport.requests import Request
11
  from googleapiclient.discovery import build
12
  from flask import Flask
13
 
14
  # ------------------------------------------------------------
15
+ # الإعدادات الأساسية
16
  # ------------------------------------------------------------
17
  TIKTOK_USERNAME = "@a3dam_111"
 
 
 
 
18
  CUSTOM_STREAM_KEY = os.getenv("CUSTOM_STREAM_KEY", "p3dq-5cex-bc1k-vet7-bsyx")
19
+ FFMPEG_LOG = "/tmp/ffmpeg.log"
20
+ MONITOR_LOG = "/tmp/monitor.log"
21
+ LOCK_FILE = "/tmp/app_monitor_core.lock"
22
+ TOKEN_FILE = "/tmp/yt_token.pkl"
23
+ BROADCAST_FILE = "/tmp/current_broadcast.json"
24
 
25
  TIKTOK_COOKIES = os.getenv("TIKTOK_COOKIES", "")
26
  TIKTOK_COOKIES_ADULT = os.getenv("TIKTOK_COOKIES_ADULT", "")
 
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:
41
  f.write(TIKTOK_COOKIES)
42
  if TIKTOK_COOKIES_ADULT:
43
+ with open("/tmp/tiktok_cookies_adult.txt", "w") as f:
44
  f.write(TIKTOK_COOKIES_ADULT)
45
 
46
  save_cookies()
 
58
  pass
59
 
60
  def get_tiktok_stream_url():
 
 
 
61
  try:
62
  url = f"https://www.tiktok.com/{TIKTOK_USERNAME}/live"
63
+ cmd = ["streamlink", "--stream-url", url, "best,720p,480p", "--retry-streams", "5", "--retry-open", "3"]
64
+ if os.path.exists("/tmp/tiktok_cookies_adult.txt"):
65
+ cmd.extend(["--cookies", "/tmp/tiktok_cookies_adult.txt"])
66
+ elif os.path.exists("/tmp/tiktok_cookies.txt"):
67
+ cmd.extend(["--cookies", "/tmp/tiktok_cookies.txt"])
68
  result = subprocess.run(cmd, capture_output=True, text=True, timeout=25)
69
  if result.returncode == 0 and result.stdout.strip():
70
  return result.stdout.strip().split("\n")[0]
71
  except: pass
 
72
  try:
73
  url = f"https://www.tiktok.com/{TIKTOK_USERNAME}/live"
74
  cmd = ["yt-dlp", "--no-warnings", "--no-check-certificates", "--no-live-from-start", "--no-cache-dir", "--rm-cache-dir", "-f", "best", "--get-url", url]
75
+ if os.path.exists("/tmp/tiktok_cookies_adult.txt"):
76
+ cmd.extend(["--cookies", "/tmp/tiktok_cookies_adult.txt"])
77
+ elif os.path.exists("/tmp/tiktok_cookies.txt"):
78
+ cmd.extend(["--cookies", "/tmp/tiktok_cookies.txt"])
79
  result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
80
  if result.returncode == 0 and result.stdout.strip():
81
  return result.stdout.strip().split("\n")[0]
 
84
 
85
  def start_stream(stream_key, stream_url):
86
  if not stream_key or not stream_url: return None
87
+ log("🎬 بدء البث (الأبعاد الطولية الحقيقية لمنع الاقتصاص)...")
88
  rtmp_url = f"rtmp://a.rtmp.youtube.com/live2/{stream_key}"
89
 
 
 
90
  cmd = [
91
  "nice", "-n", "19", "ffmpeg", "-hide_banner", "-loglevel", "warning",
92
  "-thread_queue_size", "2048",
93
  "-user_agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
94
  "-headers", "Referer: https://www.tiktok.com/\r\n",
95
 
96
+ "-rw_timeout", "15000000",
97
+ "-fflags", "+genpts+discardcorrupt", "-flags", "+global_header", "-i", stream_url,
98
+
99
+ "-c:v", "libx264", "-preset", "ultrafast", "-b:v", "2500k", "-maxrate", "2500k", "-bufsize", "5000k",
100
+ "-pix_fmt", "yuv420p", "-g", "60", "-r", "30", "-fps_mode", "cfr",
101
+
102
+ # التعديل البرمجي هنا: تغيير الأبعاد إلى الطولية 1080x1920 وملاءمتها بالكامل لحماية الصورة من القص
103
+ "-vf", "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2,setsar=1",
104
+
105
+ "-c:a", "aac", "-b:a", "192k", "-ar", "44100", "-profile:a", "aac_low",
106
+ "-af", "aresample=async=1:first_pts=0",
107
 
108
+ "-rtmp_live", "live", "-max_muxing_queue_size", "2048", "-f", "flv", rtmp_url
 
 
 
109
  ]
110
  with open(FFMPEG_LOG, "w") as stderr_file:
111
  proc = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=stderr_file, preexec_fn=os.setsid)
112
+ log(f"✅ ffmpeg PID: {proc.pid}")
113
  return proc
114
 
115
  # ------------------------------------------------------------
116
+ # YouTube API
117
  # ------------------------------------------------------------
118
  def build_youtube_client():
119
  try:
 
140
  yt = build_youtube_client()
141
  if not yt: return None, None
142
 
143
+ baghdad_tz = timezone(timedelta(hours=3))
144
+ now = datetime.now(baghdad_tz)
145
  date_str = now.strftime('%d-%m-%Y %H:%M')
 
146
 
147
+ title = f"🔥 بث تيك توك أعدام المباشر | {date_str} 🚀"
148
  description = (
149
+ "أهلاً بكم في البث المباشر المخصص لـ دكتور أعدام.\n"
 
150
  "استمتعوا معنا بأجواء البث وتفاعلوا في التعليقات!\n\n"
151
  "🔔 لا تنسوا الاشتراك في القناة وتفعيل زر التنبيهات ليصلكم كل جديد فور انطلاق البث.\n"
152
  "شكراً لدعمكم المتواصل!\n\n"
153
+ "#دكتور_إعدام #بث_مباشر #تيك_توك #TikTokLive #الاردن #بث #مباشر #a3dam_111"
154
  )
155
 
156
  insert_body = {
157
+ "snippet": {
158
+ "title": title,
159
+ "description": description,
160
+ "scheduledStartTime": now.isoformat()
161
+ },
162
  "status": {"privacyStatus": "public"},
163
  "contentDetails": {"enableAutoStart": True, "enableAutoStop": False, "latencyPreference": "normal"}
164
  }
165
 
166
  new_broadcast = yt.liveBroadcasts().insert(part="snippet,status,contentDetails", body=insert_body).execute()
167
  bid = new_broadcast["id"]
168
+ log(f"✅ بث جديد: {bid} | {title}")
169
 
170
+ # ملاحظة: يوتيوب يتعرف تلقائياً على البث الطولي بناءً على أبعاد دفق الـ ffmpeg المرسل (1080x1920)
171
  stream_insert = yt.liveStreams().insert(
172
  part="snippet,cdn,contentDetails",
173
+ body={"snippet": {"title": "TikTok Stream Key"}, "cdn": {"ingestionType": "rtmp", "resolution": "1080p", "frameRate": "30fps"}, "contentDetails": {"isReusable": True}}
174
  ).execute()
175
  stream_id = stream_insert["id"]
176
  stream_key = stream_insert["cdn"]["ingestionInfo"]["streamName"]
 
188
  try:
189
  yt = build_youtube_client()
190
  if not yt: return
191
+ broadcasts = yt.liveBroadcasts().list(mine=True, part="id,status").execute()
 
192
  for item in broadcasts.get("items", []):
193
  bid = item["id"]
194
  status = item.get("status", {}).get("lifeCycleStatus", "")
195
+ if status in ["ready", "created", "live", "testing"]:
 
 
 
196
  try:
197
  yt.liveBroadcasts().transition(broadcastStatus="complete", id=bid, part="status").execute()
 
198
  except: pass
199
  except: pass
200
 
 
208
  if os.path.exists(BROADCAST_FILE): os.remove(BROADCAST_FILE)
209
 
210
  # ------------------------------------------------------------
211
+ # المحرك الرئيسي
212
  # ------------------------------------------------------------
213
  def monitor_loop():
214
+ time.sleep(2)
215
+ if os.path.exists(LOCK_FILE): return
 
 
 
 
 
 
 
 
216
  with open(LOCK_FILE, "w") as f: f.write(str(os.getpid()))
 
217
  finalize_all_broadcasts()
218
  ffmpeg_proc = None
219
  was_streaming = False
 
221
  current_stream_key = None
222
  current_broadcast_id = None
223
 
 
 
224
  while True:
225
  try:
226
  stream_url = get_tiktok_stream_url()
 
259
  if __name__ == "__main__":
260
  t = threading.Thread(target=monitor_loop, daemon=True)
261
  t.start()
 
 
262
  app.run(host="0.0.0.0", port=7860, debug=False)