Update app.py
Browse files
app.py
CHANGED
|
@@ -12,81 +12,77 @@ def fetch_chapters_range(base_url, start_ch, end_ch):
|
|
| 12 |
start = int(start_ch)
|
| 13 |
end = int(end_ch)
|
| 14 |
|
| 15 |
-
if end < start:
|
| 16 |
-
return None, "❌ خطأ: رقم الفصل النهائي أصغر من البداية!"
|
| 17 |
-
|
| 18 |
-
if (end - start) > 20:
|
| 19 |
-
return None, "⚠️ الحد الأقصى للتجميع هو 20 فصل في المرة الواحدة لضمان استقرار السيرفر."
|
| 20 |
-
|
| 21 |
all_imgs = []
|
| 22 |
log_messages = []
|
| 23 |
-
|
| 24 |
-
# إعداد الجلسة والهيدرز لكسر الحماية
|
| 25 |
session = requests.Session()
|
|
|
|
|
|
|
| 26 |
headers = {
|
| 27 |
-
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
| 28 |
-
'Accept': '
|
| 29 |
-
'Referer':
|
| 30 |
-
'
|
| 31 |
}
|
| 32 |
|
| 33 |
base_url = base_url.strip().rstrip('/')
|
| 34 |
|
| 35 |
for i in range(start, end + 1):
|
| 36 |
-
# ت
|
| 37 |
ch_url = f"{base_url}/chapter-{i}/"
|
| 38 |
-
log_messages.append(f"⏳ جاري فحص الفصل {i}
|
| 39 |
|
| 40 |
try:
|
| 41 |
-
response = session.get(ch_url, headers=headers, timeout=
|
| 42 |
if response.status_code != 200:
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
|
|
|
| 46 |
soup = BeautifulSoup(response.text, 'html.parser')
|
| 47 |
|
| 48 |
-
#
|
| 49 |
-
|
| 50 |
-
|
| 51 |
|
| 52 |
-
images = target.find_all('img')
|
| 53 |
chapter_count = 0
|
| 54 |
-
|
| 55 |
for img in images:
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
if img_url:
|
| 58 |
-
|
|
|
|
|
|
|
| 59 |
if not img_url.startswith('http'):
|
| 60 |
img_url = "https:" + img_url if img_url.startswith('//') else img_url
|
| 61 |
|
| 62 |
-
# تصفية ال
|
| 63 |
-
if any(x in img_url.lower() for x in ["logo", "
|
| 64 |
continue
|
| 65 |
|
| 66 |
try:
|
| 67 |
-
img_res = session.get(img_url, headers=headers, timeout=
|
| 68 |
-
if img_res.status_code == 200:
|
| 69 |
image = Image.open(io.BytesIO(img_res.content)).convert('RGB')
|
| 70 |
all_imgs.append(image)
|
| 71 |
chapter_count += 1
|
| 72 |
except:
|
| 73 |
continue
|
| 74 |
|
| 75 |
-
|
| 76 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
|
| 78 |
except Exception as e:
|
| 79 |
log_messages.append(f"❌ خطأ في الفصل {i}: {str(e)}")
|
| 80 |
|
| 81 |
-
if not all_imgs:
|
| 82 |
-
return None, "\n".join(log_messages) + "\n\n❌ فشل استخراج أي صور. تأكد من رابط المانهوا الرئيسي."
|
| 83 |
-
|
| 84 |
-
# إنشاء ملف PDF المجمع
|
| 85 |
-
output_filename = f"manga_batch_{start}_{end}.pdf"
|
| 86 |
-
all_imgs[0].save(output_filename, save_all=True, append_images=all_imgs[1:], format='PDF')
|
| 87 |
-
|
| 88 |
-
return output_filename, "\n".join(log_messages) + f"\n\n✨ مبروك! تم تجميع {len(all_imgs)} صورة بنجاح."
|
| 89 |
-
|
| 90 |
# واجهة المستخدم (UI) بنظام Gradio
|
| 91 |
with gr.Blocks(theme=gr.themes.Monochrome(), title="Manga Bulk Downloader") as demo:
|
| 92 |
gr.Markdown("# 📚 مجمع فصول المانهوا الذكي (Team X & Azora)")
|
|
|
|
| 12 |
start = int(start_ch)
|
| 13 |
end = int(end_ch)
|
| 14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
all_imgs = []
|
| 16 |
log_messages = []
|
|
|
|
|
|
|
| 17 |
session = requests.Session()
|
| 18 |
+
|
| 19 |
+
# هيدرز قوية جداً لتقليد متصفح حقيقي
|
| 20 |
headers = {
|
| 21 |
+
'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',
|
| 22 |
+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
|
| 23 |
+
'Referer': base_url,
|
| 24 |
+
'Accept-Language': 'ar,en-US;q=0.7,en;q=0.3',
|
| 25 |
}
|
| 26 |
|
| 27 |
base_url = base_url.strip().rstrip('/')
|
| 28 |
|
| 29 |
for i in range(start, end + 1):
|
| 30 |
+
# تجربة نمط الرابط الخاص بأوليمبوس (غالباً يكون رقم فقط أو chapter-X)
|
| 31 |
ch_url = f"{base_url}/chapter-{i}/"
|
| 32 |
+
log_messages.append(f"⏳ جاري فحص: الفصل {i}")
|
| 33 |
|
| 34 |
try:
|
| 35 |
+
response = session.get(ch_url, headers=headers, timeout=20)
|
| 36 |
if response.status_code != 200:
|
| 37 |
+
# محاولة تجربة الرابط بدون كلمة chapter (بعض المواقع تضع الرقم فقط)
|
| 38 |
+
ch_url = f"{base_url}/{i}/"
|
| 39 |
+
response = session.get(ch_url, headers=headers, timeout=20)
|
| 40 |
+
|
| 41 |
soup = BeautifulSoup(response.text, 'html.parser')
|
| 42 |
|
| 43 |
+
# بحث مكثف عن الصور في كل الأماكن المحتملة
|
| 44 |
+
# أوليمبوس غالباً يضع الصور داخل div باسم 'rd-host' أو داخل برمجية JavaScript
|
| 45 |
+
images = soup.find_all('img')
|
| 46 |
|
|
|
|
| 47 |
chapter_count = 0
|
|
|
|
| 48 |
for img in images:
|
| 49 |
+
# فحص كل السمات الممكنة لرابط الصورة
|
| 50 |
+
img_url = (img.get('src') or
|
| 51 |
+
img.get('data-src') or
|
| 52 |
+
img.get('data-lazy-src') or
|
| 53 |
+
img.get('data-full-url') or
|
| 54 |
+
img.get('srcset')) # بعض المواقع تستخدم srcset
|
| 55 |
+
|
| 56 |
if img_url:
|
| 57 |
+
# تنظيف الرابط من المسافات أو الرموز الزائدة
|
| 58 |
+
img_url = img_url.split(' ')[0].strip()
|
| 59 |
+
|
| 60 |
if not img_url.startswith('http'):
|
| 61 |
img_url = "https:" + img_url if img_url.startswith('//') else img_url
|
| 62 |
|
| 63 |
+
# تصفية الصور التي ليست مانهوا (أيقونات، لوجو، صور صغيرة)
|
| 64 |
+
if any(x in img_url.lower() for x in ["logo", "icon", "avatar", "bg", "button", "loader"]):
|
| 65 |
continue
|
| 66 |
|
| 67 |
try:
|
| 68 |
+
img_res = session.get(img_url, headers=headers, timeout=15)
|
| 69 |
+
if img_res.status_code == 200 and len(img_res.content) > 10000: # التأكد أنها صورة حقيقية وليست بكسل صغير
|
| 70 |
image = Image.open(io.BytesIO(img_res.content)).convert('RGB')
|
| 71 |
all_imgs.append(image)
|
| 72 |
chapter_count += 1
|
| 73 |
except:
|
| 74 |
continue
|
| 75 |
|
| 76 |
+
if chapter_count > 0:
|
| 77 |
+
log_messages.append(f"✅ تم سحب {chapter_count} صورة من الفصل {i}")
|
| 78 |
+
else:
|
| 79 |
+
log_messages.append(f"⚠️ لم نجد صوراً في الفصل {i}، قد يكون المحتوى مشفراً.")
|
| 80 |
+
|
| 81 |
+
time.sleep(2) # زيادة وقت الانتظار لتجنب حماية Cloudflare في أوليمبوس
|
| 82 |
|
| 83 |
except Exception as e:
|
| 84 |
log_messages.append(f"❌ خطأ في الفصل {i}: {str(e)}")
|
| 85 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
# واجهة المستخدم (UI) بنظام Gradio
|
| 87 |
with gr.Blocks(theme=gr.themes.Monochrome(), title="Manga Bulk Downloader") as demo:
|
| 88 |
gr.Markdown("# 📚 مجمع فصول المانهوا الذكي (Team X & Azora)")
|