iq7se2 commited on
Commit
73e87db
·
verified ·
1 Parent(s): a5a71d2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +68 -90
app.py CHANGED
@@ -1,5 +1,5 @@
1
  import gradio as gr
2
- import requests
3
  from bs4 import BeautifulSoup
4
  import img2pdf
5
  import io
@@ -12,111 +12,89 @@ def fetch_chapters_range(base_url, start_ch, end_ch):
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)")
89
- gr.Markdown("أدخل الرابط الرئيسي للمانهوا وحدد مدى الفصول لتحميلها في ملف PDF واحد.")
 
 
 
 
 
 
 
90
 
91
  with gr.Row():
92
- url_input = gr.Textbox(
93
- label="رابط المانهوا الرئيسي",
94
- placeholder="مثال: https://teamx.org/series/nano-machine",
95
- scale=3
96
- )
97
 
98
  with gr.Row():
99
- start_ch = gr.Number(label="بداية من فصل", value=1, precision=0)
100
- end_ch = gr.Number(label="إلى فصل", value=5, precision=0)
101
 
102
- with gr.Column():
103
- btn = gr.Button("🚀 ابدأ التجميع والتحويل الآن", variant="primary")
104
-
105
- # قسم أرباح Adsterra
106
- gr.HTML("""
107
- <div style='background: #121212; padding: 20px; border: 2px solid #ff4500; border-radius: 12px; text-align: center;'>
108
- <p style='color: #ff4500; font-size: 18px; font-weight: bold; margin-bottom: 10px;'>🚨 تنبيه السيرفر</p>
109
- <p style='color: #ffffff; margin-bottom: 15px;'>لضمان عدم توقف التحميل التلقائي، يرجى تفعيل "رابط التحقق" من الراعي الرسمي:</p>
110
- <a href='ضع_رابط_ADSTERRA_هنا' target='_blank'
111
- style='background: #ff4500; color: white; padding: 10px 25px; text-decoration: none; border-radius: 5px; font-weight: bold; display: inline-block;'>
112
- ✅ اضغط هنا لتفعيل التحميل السريع
113
- </a>
114
- </div>
115
- """)
116
-
117
- status_output = gr.Textbox(label="سجل العملية (Log)", interactive=False, lines=8)
118
- file_output = gr.File(label="تحميل ملف الـ PDF المجمع")
119
 
120
  btn.click(fn=fetch_chapters_range, inputs=[url_input, start_ch, end_ch], outputs=[file_output, status_output])
121
 
122
- demo.launch()
 
 
1
  import gradio as gr
2
+ import cloudscraper
3
  from bs4 import BeautifulSoup
4
  import img2pdf
5
  import io
 
12
  start = int(start_ch)
13
  end = int(end_ch)
14
 
15
+ if end < start:
16
+ return None, "❌ خطأ: رقم الفصل النهائي أصغر من البداية!"
17
+
18
  all_imgs = []
19
  log_messages = []
 
20
 
21
+ # استخدام cloudscraper بدلاً من requests العادي لتجاوز حماية أوليمبوس
22
+ scraper = cloudscraper.create_scraper()
23
+
 
 
 
 
 
24
  base_url = base_url.strip().rstrip('/')
25
 
26
  for i in range(start, end + 1):
27
+ # تجربة أنماط روابط مختلفة (أوليمبوس وتيم إكس)
28
+ ch_urls = [f"{base_url}/chapter-{i}/", f"{base_url}/{i}/", f"{base_url}/فصل-{i}/"]
29
+ success = False
30
 
31
+ for url in ch_urls:
32
+ try:
33
+ log_messages.append(f"⏳ فحص: {url}")
34
+ response = scraper.get(url, timeout=20)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
+ if response.status_code == 200:
37
+ soup = BeautifulSoup(response.text, 'html.parser')
38
+ # البحث عن الصور في الأماكن المحتملة (أوليمبوس يستخدم كلاسات متغيرة)
39
+ images = soup.find_all('img')
40
+ chapter_count = 0
 
41
 
42
+ for img in images:
43
+ img_url = img.get('src') or img.get('data-src') or img.get('data-lazy-src')
44
+ if img_url:
45
+ img_url = img_url.strip()
46
+ if not img_url.startswith('http'):
47
+ img_url = "https:" + img_url if img_url.startswith('//') else img_url
48
+
49
+ if any(x in img_url.lower() for x in ["logo", "icon", "avatar", "bg", "banner"]):
50
+ continue
51
+
52
+ try:
53
+ img_res = scraper.get(img_url, timeout=15)
54
+ if img_res.status_code == 200 and len(img_res.content) > 10000:
55
+ image = Image.open(io.BytesIO(img_res.content)).convert('RGB')
56
+ all_imgs.append(image)
57
+ chapter_count += 1
58
+ except: continue
59
 
60
+ if chapter_count > 0:
61
+ log_messages.append(f"✅ تم سحب {chapter_count} صورة من الفصل {i}")
62
+ success = True
63
+ break # توقف عن تجربة الأنماط الأخرى لهذا الفصل
64
+ except: continue
65
+
66
+ if not success:
67
+ log_messages.append(f"⚠️ تعذر الوصول للفصل {i}")
68
+
69
+ time.sleep(2) # حماية من الحظر
 
 
 
 
 
 
 
 
70
 
71
+ if not all_imgs:
72
+ return None, "\n".join(log_messages) + "\n\n❌ لم يتم العثور على صور. تأكد من الرابط!"
73
+
74
+ output_filename = f"manga_collection_{int(time.time())}.pdf"
75
+ all_imgs[0].save(output_filename, save_all=True, append_images=all_imgs[1:], format='PDF')
76
+
77
+ return output_filename, "\n".join(log_messages) + "\n\n✨ تم التجميع بنجاح!"
78
+
79
+ # تصميم الواجهة مع إصلاح خطأ Theme
80
+ with gr.Blocks(title="Manga Downloader") as demo:
81
+ gr.Markdown("# 📚 محمل المانهوا الشامل")
82
 
83
  with gr.Row():
84
+ url_input = gr.Textbox(label="رابط المانهوا الرئيسي", placeholder="https://olympustaff.com/series/demonic-emperor/")
 
 
 
 
85
 
86
  with gr.Row():
87
+ start_ch = gr.Number(label="من فصل", value=1)
88
+ end_ch = gr.Number(label="إلى فصل", value=2)
89
 
90
+ btn = gr.Button("🚀 ابدأ التحميل", variant="primary")
91
+
92
+ gr.HTML("<div style='text-align:center'><a href='YOUR_ADSTERRA_LINK'>📥 اضغط هنا لتسريع التحميل (إعلان)</a></div>")
93
+
94
+ status_output = gr.Textbox(label="سجل العملية", lines=5)
95
+ file_output = gr.File(label="الملف الجاهز")
 
 
 
 
 
 
 
 
 
 
 
96
 
97
  btn.click(fn=fetch_chapters_range, inputs=[url_input, start_ch, end_ch], outputs=[file_output, status_output])
98
 
99
+ # نقل الـ theme هنا كما يطلب Gradio 6
100
+ demo.launch(ssr_mode=False)