Spaces:
Paused
Paused
File size: 5,489 Bytes
497c2d2 a418bf8 497c2d2 4ee4840 a8358bd 4ee4840 a8358bd 4ee4840 a8358bd 497c2d2 a8358bd a418bf8 6e81ee6 275274f a8358bd 6e81ee6 a418bf8 6e81ee6 a418bf8 a8358bd 497c2d2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | const express = require('express');
const cors = require('cors');
const { gotScraping } = require('got-scraping');
const cheerio = require('cheerio');
const axios = require('axios');
const app = express();
const PORT = process.env.PORT || 7860; // Port default Hugging Face
app.use(cors());
app.use(express.json());
// --- Copy & Paste Fungsi Scraping Kamu di Sini ---
const BASE_URL = 'https://04x.manhwaland.land';
const SCRAPER_HEADERS = {
'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',
'referer': BASE_URL,
'accept-language': 'en-US,en;q=0.9,id;q=0.8',
};
async function fetchHtml(url) {
const response = await gotScraping({
url,
headers: SCRAPER_HEADERS,
retry: { limit: 3, methods: ['GET'] },
timeout: { request: 10000 }
});
return response.body;
}
async function scrapeHome() {
const html = await fetchHtml(BASE_URL);
const $ = cheerio.load(html);
const results = [];
$('.listupd .bs').each((i, el) => {
results.push({
title: $(el).find('.tt').text().trim(),
url: $(el).find('a').attr('href'),
image: $(el).find('img').attr('src'),
type: $(el).find('.type').text().trim(),
chapter: $(el).find('.epxs').text().trim()
});
});
return results;
}
async function searchManga(query) {
const url = `${BASE_URL}/?s=${encodeURIComponent(query)}`;
const html = await fetchHtml(url);
const $ = cheerio.load(html);
const results = [];
$('.listupd .bs').each((i, el) => {
results.push({
title: $(el).find('.tt').text().trim(),
url: $(el).find('a').attr('href'),
image: $(el).find('img').attr('src'),
rating: $(el).find('.numscore').text().trim()
});
});
return results;
}
async function scrapeMangaDetail(url) {
const html = await fetchHtml(url);
const $ = cheerio.load(html);
const chapters = [];
$('#chapterlist li').each((i, el) => {
chapters.push({
title: $(el).find('.chapternum').text().trim(),
url: $(el).find('a').attr('href'),
date: $(el).find('.chapterdate').text().trim()
});
});
return {
title: $('.entry-title').text().trim(),
alternative: $('.alternative').text().trim(),
description: $('.entry-content p').text().trim(),
cover: $('.thumb img').attr('src'),
genres: $('.mgen a').map((i, el) => $(el).text()).get(),
status: $('.imptdt:contains("Status") i').text().trim(),
author: $('.imptdt:contains("Author") i').text().trim(),
chapters: chapters.reverse()
};
}
async function scrapeChapterImages(chapterUrl) {
try {
const { data: html } = await axios.get(chapterUrl, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36'
}
});
// 1. Ambil teks di antara 'ts_reader.run(' dan ');'
const startMarker = 'ts_reader.run(';
const endMarker = ');';
const startIndex = html.indexOf(startMarker);
const endIndex = html.indexOf(endMarker, startIndex);
if (startIndex === -1 || endIndex === -1) {
console.log("Gagal menemukan pola ts_reader.run");
return [];
}
// 2. Potong string untuk mendapatkan JSON-nya saja
const jsonString = html.substring(startIndex + startMarker.length, endIndex);
// 3. Parse JSON
const data = JSON.parse(jsonString);
// 4. Ambil array images dari sources
// Sesuai source code, data.sources[0].images adalah array URL-nya
if (data.sources && data.sources.length > 0) {
return data.sources[0].images;
}
return [];
} catch (error) {
console.error("Error scraping:", error.message);
return [];
}
}
app.get('/home', async (req, res) => {
try {
const data = await scrapeHome();
res.json({ status: 'success', data });
} catch (err) {
res.status(500).json({ status: 'error', message: err.message });
}
});
app.get('/search', async (req, res) => {
try {
const { q } = req.query;
if (!q) return res.status(400).json({ message: 'Query parameter "q" is required' });
const data = await searchManga(q);
res.json({ status: 'success', data });
} catch (err) {
res.status(500).json({ status: 'error', message: err.message });
}
});
app.get('/detail', async (req, res) => {
try {
const { url } = req.query;
if (!url) return res.status(400).json({ message: 'URL is required' });
const data = await scrapeMangaDetail(url);
res.json({ status: 'success', data });
} catch (err) {
res.status(500).json({ status: 'error', message: err.message });
}
});
app.get('/chapter', async (req, res) => {
try {
const { url } = req.query;
if (!url) return res.status(400).json({ message: 'URL is required' });
const data = await scrapeChapterImages(url);
res.json({ status: 'success', data });
} catch (err) {
res.status(500).json({ status: 'error', message: err.message });
}
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
|