Reoo88 commited on
Commit
98ba79d
·
verified ·
1 Parent(s): b45d162

Update streamlit_app.py

Browse files
Files changed (1) hide show
  1. streamlit_app.py +63 -81
streamlit_app.py CHANGED
@@ -63,19 +63,20 @@ def get_tiktok_stream_url():
63
  pass
64
  return None
65
 
66
- def analyze_stream(stream_url):
67
- """تحليل التدفق باستخدام ffprobe واستخراج معلومات الترميز"""
68
- log("🔍 جاري تحليل التدفق من TikTok...")
69
  cmd = [
70
  "ffprobe",
71
  "-v", "quiet",
72
  "-print_format", "json",
73
  "-show_streams",
74
- "-analyzeduration", "10000000", # تحليل 10 ثوانٍ
 
75
  stream_url
76
  ]
77
  try:
78
- result = subprocess.run(cmd, capture_output=True, text=True, timeout=20)
79
  if result.returncode == 0:
80
  data = json.loads(result.stdout)
81
  with open(PROBE_LOG, "w") as f:
@@ -90,24 +91,64 @@ def analyze_stream(stream_url):
90
  audio_info = s
91
  return video_info, audio_info
92
  except Exception as e:
93
- log(f"⚠️ فشل التحليل: {e}")
94
  return None, None
95
 
96
  def build_ffmpeg_command(stream_key, stream_url, video_info, audio_info):
97
- """بناء أمر ffmpeg بناءً على نتائج التحليل"""
98
- if not video_info:
99
- log("❌ لم يتم العثور على تدفق فيديو!")
100
- return None
101
-
102
- codec_v = video_info.get("codec_name", "unknown")
103
- codec_a = audio_info.get("codec_name", "unknown") if audio_info else "none"
104
- sample_rate = audio_info.get("sample_rate", "44100") if audio_info else "44100"
105
- bitrate_a = int(audio_info.get("bit_rate", 0)) // 1000 if audio_info else 0
106
-
107
- log(f"📊 فيديو: {codec_v} | صوت: {codec_a} @ {bitrate_a}kbps")
108
-
109
  rtmp_url = f"rtmp://a.rtmp.youtube.com/live2/{stream_key}"
110
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  cmd = [
112
  "nice", "-n", "19", "ffmpeg",
113
  "-hide_banner", "-loglevel", "verbose",
@@ -119,85 +160,26 @@ def build_ffmpeg_command(stream_key, stream_url, video_info, audio_info):
119
  "-i", stream_url,
120
  "-err_detect", "ignore_err",
121
  ]
122
-
123
- # فيديو
124
- if codec_v in ["h264", "hevc"]:
125
- # نسخ مباشر إن أمكن، لكننا نحتاج الفلتر للأبعاد، فلا يمكن النسخ الكامل
126
- # لذا سنستخدم libx264 دائمًا مع ضبط الجودة
127
- video_opts = [
128
- "-vf", "scale=1080:1920:force_original_aspect_ratio=increase,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black:eval=frame",
129
- "-c:v", "libx264",
130
- "-preset", "ultrafast",
131
- "-crf", "18",
132
- "-maxrate", "8000k",
133
- "-minrate", "5000k",
134
- "-bufsize", "16000k",
135
- "-g", "60",
136
- "-vsync", "2",
137
- "-x264-params", "nal-hrd=cbr",
138
- "-pix_fmt", "yuv420p",
139
- "-color_range", "tv",
140
- "-color_primaries", "bt709",
141
- "-color_trc", "bt709",
142
- "-colorspace", "bt709",
143
- ]
144
- else:
145
- # ترميز غير متوقع، نستخدم نفس الإعدادات الاحتياطية
146
- video_opts = [
147
- "-c:v", "libx264",
148
- "-preset", "ultrafast",
149
- "-crf", "18",
150
- "-maxrate", "8000k",
151
- "-minrate", "5000k",
152
- "-bufsize", "16000k",
153
- "-g", "60",
154
- "-vsync", "2",
155
- ]
156
  cmd.extend(video_opts)
157
-
158
- # صوت
159
- if codec_a in ["aac"] and bitrate_a <= 192:
160
- # نسخ الصوت مباشرة
161
- audio_opts = ["-c:a", "copy"]
162
- log("🔊 نسخ الصوت مباشرة (جودة مناسبة)")
163
- else:
164
- # إعادة ترميز الصوت مع فلتر مناسب
165
- audio_opts = [
166
- "-c:a", "aac",
167
- "-b:a", "192k",
168
- "-ac", "2",
169
- "-ar", str(sample_rate),
170
- "-async", "1",
171
- ]
172
- # إذا كان الصوت به ذروات عالية (نفترض ذلك إذا كان معدل البت > 192)
173
- if bitrate_a > 192:
174
- audio_opts.extend(["-af", "alimiter=limit=0.95:attack=5:release=50"])
175
- log("🔊 إضافة فلتر الصوت للذروات العالية")
176
  cmd.extend(audio_opts)
177
-
178
- # إرسال
179
  cmd.extend([
180
  "-max_muxing_queue_size", "65535",
181
  "-max_delay", "5000000",
182
  "-flvflags", "no_duration_filesize+add_keyframe_index",
183
  "-f", "flv", rtmp_url
184
  ])
185
-
186
  return cmd
187
 
188
  def start_stream_adaptive(stream_key, stream_url):
189
  if not stream_key or not stream_url:
190
  return None
191
 
192
- # تحليل التدفق أولاً
193
- video_info, audio_info = analyze_stream(stream_url)
194
-
195
- # بناء الأمر
196
  final_cmd = build_ffmpeg_command(stream_key, stream_url, video_info, audio_info)
197
  if not final_cmd:
198
  return None
199
 
200
- log("🎬 بدء البث الذكي...")
201
  with open(FFMPEG_LOG, "w") as stderr_file:
202
  proc = subprocess.Popen(final_cmd, stdout=subprocess.DEVNULL,
203
  stderr=stderr_file, preexec_fn=os.setsid)
@@ -222,7 +204,7 @@ def run_monitor():
222
  was_streaming = False
223
  consecutive_fails = 0
224
 
225
- log("🚀 محرك البث الذكي (تحليل تلقائي)")
226
 
227
  while True:
228
  try:
@@ -263,7 +245,7 @@ def run_monitor():
263
  time.sleep(10)
264
 
265
  if __name__ == "__main__":
266
- log("✅ بدء تشغيل المحرك الذكي")
267
  t = threading.Thread(target=run_monitor, daemon=True)
268
  t.start()
269
  while True:
 
63
  pass
64
  return None
65
 
66
+ def analyze_stream_deep(stream_url):
67
+ """تحليل عميق للتدفق مع وقت أطول وخيارات إضافية"""
68
+ log("🔍 جاري تحليل عميق للتدفق...")
69
  cmd = [
70
  "ffprobe",
71
  "-v", "quiet",
72
  "-print_format", "json",
73
  "-show_streams",
74
+ "-analyzeduration", "30000000", # 30 ثانية تحليل
75
+ "-probesize", "20000000",
76
  stream_url
77
  ]
78
  try:
79
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=45)
80
  if result.returncode == 0:
81
  data = json.loads(result.stdout)
82
  with open(PROBE_LOG, "w") as f:
 
91
  audio_info = s
92
  return video_info, audio_info
93
  except Exception as e:
94
+ log(f"⚠️ فشل التحليل العميق: {e}")
95
  return None, None
96
 
97
  def build_ffmpeg_command(stream_key, stream_url, video_info, audio_info):
98
+ """بناء الأمر بناءً على التحليل العميق أو خطة احتياطية قوية"""
 
 
 
 
 
 
 
 
 
 
 
99
  rtmp_url = f"rtmp://a.rtmp.youtube.com/live2/{stream_key}"
100
 
101
+ # ==================== الصوت ====================
102
+ if audio_info:
103
+ codec_a = audio_info.get("codec_name", "unknown")
104
+ # محاولة الحصول على معدل بت حقيقي، إذا كان 0 نعتمد خطة احتياطية
105
+ bitrate_a = int(audio_info.get("bit_rate", 0)) // 1000 if audio_info.get("bit_rate") else 0
106
+ log(f"📊 الصوت: {codec_a} | معدل البت: {bitrate_a}kbps")
107
+ if codec_a == "aac" and bitrate_a >= 128:
108
+ audio_opts = ["-c:a", "copy"]
109
+ log("🔊 نسخ الصوت مباشرة (جودة كافية)")
110
+ else:
111
+ audio_opts = [
112
+ "-c:a", "aac",
113
+ "-b:a", "192k",
114
+ "-ac", "2",
115
+ "-ar", "44100",
116
+ "-async", "1",
117
+ "-af", "alimiter=limit=0.95:attack=5:release=50,loudnorm=I=-16:TP=-1.5:LRA=11"
118
+ ]
119
+ log("🔊 إعادة ترميز الصوت مع فلتر تطبيع واحترافي")
120
+ else:
121
+ # لا توجد معلومات صوت، خطة احتياطية
122
+ audio_opts = [
123
+ "-c:a", "aac",
124
+ "-b:a", "192k",
125
+ "-ac", "2",
126
+ "-ar", "44100",
127
+ "-async", "1",
128
+ "-af", "alimiter=limit=0.95:attack=5:release=50,loudnorm=I=-16:TP=-1.5:LRA=11"
129
+ ]
130
+ log("🔊 لم يتم تحليل الصوت – استخدام خطة احتياطية قوية")
131
+
132
+ # ==================== الفيديو ====================
133
+ # نستخدم دائمًا إعدادات الفيديو المثبتة مع فلتر الأبعاد
134
+ video_opts = [
135
+ "-vf", "scale=1080:1920:force_original_aspect_ratio=increase,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black:eval=frame",
136
+ "-c:v", "libx264",
137
+ "-preset", "ultrafast",
138
+ "-crf", "18",
139
+ "-maxrate", "8000k",
140
+ "-minrate", "5000k",
141
+ "-bufsize", "16000k",
142
+ "-g", "60",
143
+ "-vsync", "2",
144
+ "-x264-params", "nal-hrd=cbr",
145
+ "-pix_fmt", "yuv420p",
146
+ "-color_range", "tv",
147
+ "-color_primaries", "bt709",
148
+ "-color_trc", "bt709",
149
+ "-colorspace", "bt709",
150
+ ]
151
+
152
  cmd = [
153
  "nice", "-n", "19", "ffmpeg",
154
  "-hide_banner", "-loglevel", "verbose",
 
160
  "-i", stream_url,
161
  "-err_detect", "ignore_err",
162
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  cmd.extend(video_opts)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  cmd.extend(audio_opts)
 
 
165
  cmd.extend([
166
  "-max_muxing_queue_size", "65535",
167
  "-max_delay", "5000000",
168
  "-flvflags", "no_duration_filesize+add_keyframe_index",
169
  "-f", "flv", rtmp_url
170
  ])
 
171
  return cmd
172
 
173
  def start_stream_adaptive(stream_key, stream_url):
174
  if not stream_key or not stream_url:
175
  return None
176
 
177
+ video_info, audio_info = analyze_stream_deep(stream_url)
 
 
 
178
  final_cmd = build_ffmpeg_command(stream_key, stream_url, video_info, audio_info)
179
  if not final_cmd:
180
  return None
181
 
182
+ log("🎬 بدء البث الذكي المُحسَّن...")
183
  with open(FFMPEG_LOG, "w") as stderr_file:
184
  proc = subprocess.Popen(final_cmd, stdout=subprocess.DEVNULL,
185
  stderr=stderr_file, preexec_fn=os.setsid)
 
204
  was_streaming = False
205
  consecutive_fails = 0
206
 
207
+ log("🚀 محرك البث الذكي المُحسَّن (تحليل عميق)")
208
 
209
  while True:
210
  try:
 
245
  time.sleep(10)
246
 
247
  if __name__ == "__main__":
248
+ log("✅ بدء تشغيل المحرك الذكي المُحسَّن")
249
  t = threading.Thread(target=run_monitor, daemon=True)
250
  t.start()
251
  while True: