MahmoudNabilMohamed commited on
Commit
f1015c5
·
verified ·
1 Parent(s): 94584b6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +158 -70
app.py CHANGED
@@ -2,7 +2,6 @@ import os
2
  import time
3
  import io
4
  import base64
5
- import threading
6
 
7
  import cv2
8
  import numpy as np
@@ -75,31 +74,40 @@ class SuperUpscaler:
75
  self.onnx_session = None
76
  return False
77
 
78
- def preprocess_for_ai(self, image, target_size=256):
79
- """تحضير الصورة للنموذج."""
 
 
 
 
80
  try:
81
  rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
82
  h, w = rgb_image.shape[:2]
83
-
84
- if max(h, w) > target_size:
85
- if w > h:
86
- new_w, new_h = target_size, int(h * target_size / w)
87
- else:
88
- new_w, new_h = int(w * target_size / h), target_size
 
89
  rgb_image = cv2.resize(rgb_image, (new_w, new_h), interpolation=cv2.INTER_LANCZOS4)
90
-
91
  h, w = rgb_image.shape[:2]
 
 
92
  new_h = ((h + 3) // 4) * 4
93
  new_w = ((w + 3) // 4) * 4
94
 
95
  if new_h != h or new_w != w:
96
  rgb_image = cv2.resize(rgb_image, (new_w, new_h), interpolation=cv2.INTER_LANCZOS4)
 
97
 
 
98
  normalized = rgb_image.astype(np.float32) / 255.0
99
  transposed = np.transpose(normalized, (2, 0, 1))
100
  batched = np.expand_dims(transposed, axis=0)
101
 
102
- return batched, (rgb_image.shape[0], rgb_image.shape[1])
103
 
104
  except Exception as exc:
105
  print(f"❌ خطأ أثناء التحضير للنموذج: {exc}")
@@ -129,10 +137,13 @@ class SuperUpscaler:
129
  """رفع دقة الصورة مع معالجة خاصة للصور الكبيرة."""
130
  try:
131
  h, w = image.shape[:2]
132
- if max(h, w) > 800:
133
- print("📦 تقسيم الصورة الكبيرة إلى أجزاء...")
134
- return self.process_large_image_in_tiles(image)
135
- print("🔄 معالجة صورة واحدة عبر الذكاء الاصطناعي...")
 
 
 
136
  return self.process_single_image_with_ai(image)
137
 
138
  except Exception as exc:
@@ -142,55 +153,103 @@ class SuperUpscaler:
142
  def process_single_image_with_ai(self, image):
143
  """تشغيل النموذج على صورة واحدة."""
144
  try:
145
- input_data, preprocessed_size = self.preprocess_for_ai(image)
146
  if input_data is None:
147
  return None
148
 
149
  input_name = self.onnx_session.get_inputs()[0].name
150
  start_time = time.time()
151
  outputs = self.onnx_session.run(None, {input_name: input_data})
152
- print(f"⏱️ زمن الاستدلال: {time.time() - start_time:.2f} ثانية")
153
-
154
- return self.postprocess_from_ai(outputs[0], preprocessed_size)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
 
156
  except Exception as exc:
157
  print(f"❌ خطأ أثناء الاستدلال: {exc}")
158
  return None
159
 
160
- def process_large_image_in_tiles(self, image):
161
- """تقسيم الصورة الكبيرة إلى أجزاء ومعالجتها."""
162
  try:
163
  h, w = image.shape[:2]
164
- tile_size = 256
165
- overlap = 32
166
-
167
  print(f"📦 تقسيم صورة {w}x{h} إلى أجزاء {tile_size}x{tile_size}")
168
 
169
  output_h, output_w = h * self.scale, w * self.scale
170
  final_image = np.zeros((output_h, output_w, 3), dtype=np.uint8)
 
171
 
172
  processed_tiles = 0
173
- total_tiles = ((h + tile_size - 1) // tile_size) * ((w + tile_size - 1) // tile_size)
 
 
174
 
175
- for y in range(0, h, tile_size - overlap):
176
- for x in range(0, w, tile_size - overlap):
177
  y_end = min(y + tile_size, h)
178
  x_end = min(x + tile_size, w)
179
  tile = image[y:y_end, x:x_end]
180
 
 
181
  enhanced_tile = self.process_single_image_with_ai(tile)
 
182
  if enhanced_tile is not None:
183
  tile_h, tile_w = enhanced_tile.shape[:2]
184
  y_start = y * self.scale
185
  x_start = x * self.scale
186
  y_end_out = min(y_start + tile_h, output_h)
187
  x_end_out = min(x_start + tile_w, output_w)
188
- final_image[y_start:y_end_out, x_start:x_end_out] = enhanced_tile[
189
- : y_end_out - y_start, : x_end_out - x_start
190
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
 
192
  processed_tiles += 1
193
- print(f"📦 تمت معالجة {processed_tiles}/{total_tiles} من الأجزاء")
 
 
 
 
 
 
 
 
 
 
194
 
195
  print("✅ تم دمج الأجزاء بنجاح!")
196
  return final_image
@@ -199,7 +258,7 @@ class SuperUpscaler:
199
  print(f"❌ خطأ أثناء معالجة الأجزاء: {exc}")
200
  return None
201
 
202
- def postprocess_from_ai(self, output, preprocessed_size):
203
  """تحويل مخرجات النموذج إلى صورة BGR."""
204
  try:
205
  if len(output.shape) == 4:
@@ -219,25 +278,31 @@ class SuperUpscaler:
219
 
220
  def lanczos_enhanced(self, image):
221
  """رفع دقة الصورة بطريقة لانكزوس مع تحسينات إضافية."""
222
- h, w = image.shape[:2]
223
- pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
224
-
225
- if min(w, h) < 500:
226
- pil_image = pil_image.filter(ImageFilter.MedianFilter(size=3))
227
 
228
- new_size = (w * self.scale, h * self.scale)
229
- upscaled = pil_image.resize(new_size, Image.LANCZOS)
 
230
 
231
- enhancer = ImageEnhance.Sharpness(upscaled)
232
- upscaled = enhancer.enhance(1.3)
233
 
234
- enhancer = ImageEnhance.Contrast(upscaled)
235
- upscaled = enhancer.enhance(1.1)
 
236
 
237
- enhancer = ImageEnhance.Color(upscaled)
238
- upscaled = enhancer.enhance(1.05)
239
 
240
- return cv2.cvtColor(np.array(upscaled), cv2.COLOR_RGB2BGR)
 
 
 
 
 
 
241
 
242
 
243
  upscaler = SuperUpscaler()
@@ -245,7 +310,7 @@ upscaler = SuperUpscaler()
245
 
246
  @app.route("/health", methods=["GET"])
247
  def health_check():
248
- model_type = "UltraSharp AI (Always Used)" if upscaler.setup_complete else "Enhanced Traditional Upscaler"
249
  return jsonify(
250
  {
251
  "status": "running",
@@ -253,9 +318,8 @@ def health_check():
253
  "model_type": model_type,
254
  "scale_factor": upscaler.scale,
255
  "ai_model_available": upscaler.setup_complete,
256
- "ai_always_used": True,
257
  "supports_large_images": True,
258
- "message": f"Super Resolution API running with {model_type} - AI used for ALL images!",
259
  }
260
  )
261
 
@@ -264,11 +328,11 @@ def health_check():
264
  def upscale_image():
265
  try:
266
  if "image" not in request.files:
267
- return jsonify({"error": "لم يتم إرسال صورة"}), 400
268
 
269
  file = request.files["image"]
270
  if file.filename == "":
271
- return jsonify({"error": "لم يتم اختيار صورة"}), 400
272
 
273
  start_total = time.time()
274
  image_bytes = file.read()
@@ -276,24 +340,27 @@ def upscale_image():
276
  image = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
277
 
278
  if image is None:
279
- return jsonify({"error": "فشل في قراءة الصورة"}), 400
280
 
281
  original_height, original_width = image.shape[:2]
282
- print(f"📏 معالجة صورة بحجم كامل: {original_width}x{original_height}")
283
 
284
- method_used = "UltraSharp AI" if upscaler.setup_complete else "Enhanced Traditional"
 
285
  process_start = time.time()
286
  upscaled_image = upscaler.enhance_image(image)
287
  process_time = time.time() - process_start
288
 
289
  if upscaled_image is None:
290
- return jsonify({"error": "فشل في رفع دقة الصورة"}), 500
291
 
292
- print(f"✅ زمن المعالجة: {process_time:.2f} ثانية | الحجم النهائي: {upscaled_image.shape[1]}x{upscaled_image.shape[0]}")
 
293
 
 
294
  pil_image = Image.fromarray(cv2.cvtColor(upscaled_image, cv2.COLOR_BGR2RGB))
295
  buffer = io.BytesIO()
296
- pil_image.save(buffer, format="PNG", optimize=True)
297
  img_base64 = base64.b64encode(buffer.getvalue()).decode("utf-8")
298
 
299
  total_time = time.time() - start_total
@@ -304,36 +371,57 @@ def upscale_image():
304
  "message": f"تم رفع الدقة بنجاح باستخدام {method_used}!",
305
  "image": img_base64,
306
  "original_size": f"{original_width}x{original_height}",
307
- "upscaled_size": f"{upscaled_image.shape[1]}x{upscaled_image.shape[0]}",
308
  "scale_factor": upscaler.scale,
309
- "model_used": method_used,
310
  "processing_time": f"{total_time:.2f}s",
311
  "ai_used": upscaler.setup_complete,
312
- "full_quality": True,
313
  }
314
  )
315
 
316
  except Exception as exc:
317
  print(f"❌ خطأ غير متوقّع: {exc}")
318
- return jsonify({"error": f"خطأ في معالجة الصورة: {str(exc)}"}), 500
 
 
319
 
320
 
321
  @app.route("/", methods=["GET"])
322
  def home():
323
- model_status = "🤖 AI Model (Always Used)" if upscaler.setup_complete else "🔧 Enhanced Traditional"
324
  return f"""
325
- <h1>🚀 True AI Super Resolution</h1>
326
- <p>✅ الذكاء الاصطناعي يعمل لجميع الصور!</p>
327
- <p><strong>حالة النموذج:</strong> {model_status}</p>
328
- <p><strong>🎯 مضمون:</strong> جودة حقيقية لجميع الأحجام</p>
329
- <ul>
330
- <li><a href="/health">/health</a> - فحص الحالة</li>
331
- <li>/upscale - رفع دقة بـ AI (POST)</li>
332
- </ul>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
333
  """
334
 
335
 
336
  if __name__ == "__main__":
337
  port = int(os.environ.get("PORT", 7860))
338
  print(f"🚀 تشغيل Flask على المنفذ {port}...")
 
339
  app.run(host="0.0.0.0", port=port, debug=False, threaded=True)
 
2
  import time
3
  import io
4
  import base64
 
5
 
6
  import cv2
7
  import numpy as np
 
74
  self.onnx_session = None
75
  return False
76
 
77
+ def preprocess_for_ai(self, image, max_size=512):
78
+ """
79
+ تحضير الصورة للنموذج بدون تقليل الحجم الشديد.
80
+ - تحافظ على أكبر قدر من التفاصيل
81
+ - تجعل الأبعاد قابلة للقسمة على 4
82
+ """
83
  try:
84
  rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
85
  h, w = rgb_image.shape[:2]
86
+
87
+ # تقليل فقط إذا كانت الصورة كبيرة جداً (أكبر من 512px)
88
+ if max(h, w) > max_size:
89
+ scale = max_size / max(h, w)
90
+ new_w = int(w * scale)
91
+ new_h = int(h * scale)
92
+ print(f"📐 تصغير من {w}x{h} إلى {new_w}x{new_h} للمعالجة")
93
  rgb_image = cv2.resize(rgb_image, (new_w, new_h), interpolation=cv2.INTER_LANCZOS4)
94
+
95
  h, w = rgb_image.shape[:2]
96
+
97
+ # جعل الأبعاد قابلة للقسمة على 4 (متطلب النموذج)
98
  new_h = ((h + 3) // 4) * 4
99
  new_w = ((w + 3) // 4) * 4
100
 
101
  if new_h != h or new_w != w:
102
  rgb_image = cv2.resize(rgb_image, (new_w, new_h), interpolation=cv2.INTER_LANCZOS4)
103
+ print(f"📐 تعديل للأبعاد: {new_w}x{new_h}")
104
 
105
+ # تطبيع
106
  normalized = rgb_image.astype(np.float32) / 255.0
107
  transposed = np.transpose(normalized, (2, 0, 1))
108
  batched = np.expand_dims(transposed, axis=0)
109
 
110
+ return batched, (h, w)
111
 
112
  except Exception as exc:
113
  print(f"❌ خطأ أثناء التحضير للنموذج: {exc}")
 
137
  """رفع دقة الصورة مع معالجة خاصة للصور الكبيرة."""
138
  try:
139
  h, w = image.shape[:2]
140
+
141
+ # استخدام تقسيم للصور الكبيرة جداً فقط
142
+ if max(h, w) > 1024:
143
+ print("📦 الصورة كبيرة جداً - تقسيم إلى أجزاء...")
144
+ return self.process_large_image_in_tiles(image, tile_size=512, overlap=64)
145
+
146
+ print("🔄 معالجة الصورة كاملة عبر الذكاء الاصطناعي...")
147
  return self.process_single_image_with_ai(image)
148
 
149
  except Exception as exc:
 
153
  def process_single_image_with_ai(self, image):
154
  """تشغيل النموذج على صورة واحدة."""
155
  try:
156
+ input_data, original_size = self.preprocess_for_ai(image, max_size=512)
157
  if input_data is None:
158
  return None
159
 
160
  input_name = self.onnx_session.get_inputs()[0].name
161
  start_time = time.time()
162
  outputs = self.onnx_session.run(None, {input_name: input_data})
163
+ inference_time = time.time() - start_time
164
+ print(f"⏱️ زمن الاستدلال: {inference_time:.2f} ثانية")
165
+
166
+ enhanced = self.postprocess_from_ai(outputs[0])
167
+
168
+ if enhanced is not None:
169
+ # تكبير للحجم المطلوب (4x من الأصل)
170
+ orig_h, orig_w = image.shape[:2]
171
+ target_h, target_w = orig_h * self.scale, orig_w * self.scale
172
+
173
+ curr_h, curr_w = enhanced.shape[:2]
174
+
175
+ # إذا كان الحجم أصغر من المطلوب، كبّر
176
+ if curr_h < target_h or curr_w < target_w:
177
+ print(f"📐 تكبير من {curr_w}x{curr_h} إلى {target_w}x{target_h}")
178
+ enhanced = cv2.resize(enhanced, (target_w, target_h), interpolation=cv2.INTER_LANCZOS4)
179
+
180
+ return enhanced
181
+
182
+ return None
183
 
184
  except Exception as exc:
185
  print(f"❌ خطأ أثناء الاستدلال: {exc}")
186
  return None
187
 
188
+ def process_large_image_in_tiles(self, image, tile_size=512, overlap=64):
189
+ """تقسيم الصورة الكبيرة إلى أجزاء ومعالجتها بشكل محسّن."""
190
  try:
191
  h, w = image.shape[:2]
 
 
 
192
  print(f"📦 تقسيم صورة {w}x{h} إلى أجزاء {tile_size}x{tile_size}")
193
 
194
  output_h, output_w = h * self.scale, w * self.scale
195
  final_image = np.zeros((output_h, output_w, 3), dtype=np.uint8)
196
+ weight_map = np.zeros((output_h, output_w), dtype=np.float32)
197
 
198
  processed_tiles = 0
199
+ y_positions = list(range(0, h, tile_size - overlap))
200
+ x_positions = list(range(0, w, tile_size - overlap))
201
+ total_tiles = len(y_positions) * len(x_positions)
202
 
203
+ for y in y_positions:
204
+ for x in x_positions:
205
  y_end = min(y + tile_size, h)
206
  x_end = min(x + tile_size, w)
207
  tile = image[y:y_end, x:x_end]
208
 
209
+ # معالجة القطعة
210
  enhanced_tile = self.process_single_image_with_ai(tile)
211
+
212
  if enhanced_tile is not None:
213
  tile_h, tile_w = enhanced_tile.shape[:2]
214
  y_start = y * self.scale
215
  x_start = x * self.scale
216
  y_end_out = min(y_start + tile_h, output_h)
217
  x_end_out = min(x_start + tile_w, output_w)
218
+
219
+ # دمج مع وزن لتقليل الحواف
220
+ actual_tile_h = y_end_out - y_start
221
+ actual_tile_w = x_end_out - x_start
222
+
223
+ # إنشاء وزن تدريجي للحواف
224
+ fade = np.ones((actual_tile_h, actual_tile_w), dtype=np.float32)
225
+ fade_size = min(overlap * self.scale, min(actual_tile_h, actual_tile_w) // 4)
226
+
227
+ if fade_size > 0:
228
+ for i in range(fade_size):
229
+ fade[i, :] *= i / fade_size
230
+ fade[-i-1, :] *= i / fade_size
231
+ fade[:, i] *= i / fade_size
232
+ fade[:, -i-1] *= i / fade_size
233
+
234
+ for c in range(3):
235
+ final_image[y_start:y_end_out, x_start:x_end_out, c] += (
236
+ enhanced_tile[:actual_tile_h, :actual_tile_w, c] * fade
237
+ ).astype(np.uint8)
238
+
239
+ weight_map[y_start:y_end_out, x_start:x_end_out] += fade
240
 
241
  processed_tiles += 1
242
+ if processed_tiles % 5 == 0:
243
+ print(f"📦 تمت معالجة {processed_tiles}/{total_tiles} من الأجزاء")
244
+
245
+ # تطبيع حسب الوزن
246
+ for c in range(3):
247
+ final_image[:, :, c] = np.divide(
248
+ final_image[:, :, c],
249
+ weight_map,
250
+ out=np.zeros_like(final_image[:, :, c], dtype=np.uint8),
251
+ where=weight_map != 0
252
+ )
253
 
254
  print("✅ تم دمج الأجزاء بنجاح!")
255
  return final_image
 
258
  print(f"❌ خطأ أثناء معالجة الأجزاء: {exc}")
259
  return None
260
 
261
+ def postprocess_from_ai(self, output):
262
  """تحويل مخرجات النموذج إلى صورة BGR."""
263
  try:
264
  if len(output.shape) == 4:
 
278
 
279
  def lanczos_enhanced(self, image):
280
  """رفع دقة الصورة بطريقة لانكزوس مع تحسينات إضافية."""
281
+ try:
282
+ h, w = image.shape[:2]
283
+ pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
 
 
284
 
285
+ # تقليل الضوضاء للصور الصغيرة فقط
286
+ if min(w, h) < 300:
287
+ pil_image = pil_image.filter(ImageFilter.MedianFilter(size=3))
288
 
289
+ new_size = (w * self.scale, h * self.scale)
290
+ upscaled = pil_image.resize(new_size, Image.LANCZOS)
291
 
292
+ # تحسينات خفيفة
293
+ enhancer = ImageEnhance.Sharpness(upscaled)
294
+ upscaled = enhancer.enhance(1.2)
295
 
296
+ enhancer = ImageEnhance.Contrast(upscaled)
297
+ upscaled = enhancer.enhance(1.05)
298
 
299
+ return cv2.cvtColor(np.array(upscaled), cv2.COLOR_RGB2BGR)
300
+
301
+ except Exception as exc:
302
+ print(f"❌ خطأ في طريقة لانكزوس: {exc}")
303
+ # إرجاع الصورة مكبرة بطريقة بسيطة
304
+ h, w = image.shape[:2]
305
+ return cv2.resize(image, (w * self.scale, h * self.scale), interpolation=cv2.INTER_CUBIC)
306
 
307
 
308
  upscaler = SuperUpscaler()
 
310
 
311
  @app.route("/health", methods=["GET"])
312
  def health_check():
313
+ model_type = "UltraSharp AI 4x" if upscaler.setup_complete else "Enhanced Traditional Upscaler"
314
  return jsonify(
315
  {
316
  "status": "running",
 
318
  "model_type": model_type,
319
  "scale_factor": upscaler.scale,
320
  "ai_model_available": upscaler.setup_complete,
 
321
  "supports_large_images": True,
322
+ "message": f"Super Resolution API running with {model_type}",
323
  }
324
  )
325
 
 
328
  def upscale_image():
329
  try:
330
  if "image" not in request.files:
331
+ return jsonify({"success": False, "error": "لم يتم إرسال صورة"}), 400
332
 
333
  file = request.files["image"]
334
  if file.filename == "":
335
+ return jsonify({"success": False, "error": "لم يتم اختيار صورة"}), 400
336
 
337
  start_total = time.time()
338
  image_bytes = file.read()
 
340
  image = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
341
 
342
  if image is None:
343
+ return jsonify({"success": False, "error": "فشل في قراءة الصورة"}), 400
344
 
345
  original_height, original_width = image.shape[:2]
346
+ print(f"📏 معالجة صورة: {original_width}x{original_height}")
347
 
348
+ method_used = "UltraSharp AI 4x" if upscaler.setup_complete else "Enhanced Lanczos"
349
+
350
  process_start = time.time()
351
  upscaled_image = upscaler.enhance_image(image)
352
  process_time = time.time() - process_start
353
 
354
  if upscaled_image is None:
355
+ return jsonify({"success": False, "error": "فشل في رفع دقة الصورة"}), 500
356
 
357
+ final_h, final_w = upscaled_image.shape[:2]
358
+ print(f"✅ زمن المعالجة: {process_time:.2f}s | النتيجة: {final_w}x{final_h}")
359
 
360
+ # حفظ بجودة عالية
361
  pil_image = Image.fromarray(cv2.cvtColor(upscaled_image, cv2.COLOR_BGR2RGB))
362
  buffer = io.BytesIO()
363
+ pil_image.save(buffer, format="PNG", optimize=False, compress_level=1)
364
  img_base64 = base64.b64encode(buffer.getvalue()).decode("utf-8")
365
 
366
  total_time = time.time() - start_total
 
371
  "message": f"تم رفع الدقة بنجاح باستخدام {method_used}!",
372
  "image": img_base64,
373
  "original_size": f"{original_width}x{original_height}",
374
+ "upscaled_size": f"{final_w}x{final_h}",
375
  "scale_factor": upscaler.scale,
376
+ "model_type": method_used,
377
  "processing_time": f"{total_time:.2f}s",
378
  "ai_used": upscaler.setup_complete,
 
379
  }
380
  )
381
 
382
  except Exception as exc:
383
  print(f"❌ خطأ غير متوقّع: {exc}")
384
+ import traceback
385
+ traceback.print_exc()
386
+ return jsonify({"success": False, "error": f"خطأ في معالجة الصورة: {str(exc)}"}), 500
387
 
388
 
389
  @app.route("/", methods=["GET"])
390
  def home():
391
+ model_status = "🤖 UltraSharp AI 4x" if upscaler.setup_complete else "🔧 Enhanced Traditional"
392
  return f"""
393
+ <!DOCTYPE html>
394
+ <html>
395
+ <head>
396
+ <title>AI Super Resolution</title>
397
+ <style>
398
+ body {{ font-family: Arial; padding: 20px; background: #f5f5f5; }}
399
+ .container {{ max-width: 800px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; }}
400
+ h1 {{ color: #333; }}
401
+ .status {{ padding: 15px; background: #e8f5e9; border-radius: 5px; margin: 20px 0; }}
402
+ </style>
403
+ </head>
404
+ <body>
405
+ <div class="container">
406
+ <h1>🚀 AI Super Resolution API</h1>
407
+ <div class="status">
408
+ <p><strong>حالة النموذج:</strong> {model_status}</p>
409
+ <p><strong>معامل التكبير:</strong> 4x</p>
410
+ <p><strong>جاهز للاستخدام:</strong> ✅</p>
411
+ </div>
412
+ <h3>نقاط النهاية (Endpoints):</h3>
413
+ <ul>
414
+ <li><a href="/health">/health</a> - فحص حالة الخدمة</li>
415
+ <li>/upscale - رفع دقة الصورة (POST)</li>
416
+ </ul>
417
+ </div>
418
+ </body>
419
+ </html>
420
  """
421
 
422
 
423
  if __name__ == "__main__":
424
  port = int(os.environ.get("PORT", 7860))
425
  print(f"🚀 تشغيل Flask على المنفذ {port}...")
426
+ print("=" * 60)
427
  app.run(host="0.0.0.0", port=port, debug=False, threaded=True)