react / server.js
coincreator's picture
Update server.js
6e81ee6 verified
Raw
History Blame Contribute Delete
5.49 kB
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}`);
});