import axios from 'axios' import * as cheerio from 'cheerio' const BASE_URL = 'https://www.zerochan.net' const UA = 'MithraEX - kyiov' function absolutize(url) { if (!url) return null if (/^https?:\/\//i.test(url)) return url return `${BASE_URL}${url.startsWith('/') ? '' : '/'}${url}` } export async function zerochanSearch(query, page = 1) { if (!query) throw new Error('Query is required.') const url = `${BASE_URL}/search?q=${encodeURIComponent(query)}&p=${page}` const { data } = await axios.get(url, { headers: { 'User-Agent': UA, 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,webp,*/*;q=0.8' }, validateStatus: (status) => status === 200 || status === 404, timeout: 20000 }) const $ = cheerio.load(data) const results = [] $('#thumbs2 li, #thumbs li').each((_, el) => { const a = $(el).find('a.thumb').first() const img = $(el).find('img').first() const title = img.attr('alt') || a.attr('title') || $(el).find('p a').text().trim() const href = a.attr('href') const cover = absolutize(img.attr('data-src') || img.attr('src')) const id = $(el).attr('data-id') || (href ? href.replace(/[^\d]/g, '') : null) if (title && href) { results.push({ id, title, url: absolutize(href), cover, meta: img.attr('title') || null }) } }) return results } export async function zerochanDetail(urlOrId) { if (!urlOrId) throw new Error('URL or ID is required.') const url = /^https?:\/\//i.test(urlOrId) ? urlOrId : `${BASE_URL}/${urlOrId}` const { data } = await axios.get(url, { headers: { 'User-Agent': UA, 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,webp,*/*;q=0.8' }, validateStatus: (status) => status === 200 || status === 404, timeout: 20000 }) const $ = cheerio.load(data) const title = $('h1').first().text().trim() || $('.content h1').first().text().trim() || 'Zerochan Image' const cover = absolutize($('img').first().attr('data-src') || $('img').first().attr('src')) const metaText = $('img').first().attr('title') || $('.meta').text().trim() || '' return { title, cover, source: url, metaText } } export default { zerochanSearch, zerochanDetail }