'use strict'; // First-party parser; inputs are fetched by the paced source broker, not here. const decode = value => String(value).replace(/&/g, '&').replace(/'|'/gi, "'") .replace(/"/g, '"').replace(/<[^>]+>/g, '').trim(); function catalog({ html, url }) { const sourceId = new URL(url).pathname.split('/')[2]; const title = decode(html.match(/]*>([\s\S]*?)<\/h1>/i)?.[1] || 'Untitled'); const chapters = []; const re = /]*href="(\/chapters\/(\d+-\d+)\/[^"?#]+)"[^>]*>([\s\S]*?)<\/a>/gi; for (const match of html.matchAll(re)) { if (match[2].split('-')[0] !== sourceId) continue; const label = decode(match[3]); const number = label.match(/^Chapter\s+([0-9]+(?:\.[0-9]+)?)(?:\b|$)/i)?.[1]; if (!number) continue; chapters.push({ id: match[2], number, title: '', url: new URL(match[1], url).href, raw: label }); } return { title, chapters: [...new Map(chapters.map(ref => [ref.id, ref])).values()], chapterLinks: [...html.matchAll(/href="\/chapters\/\d+-\d+\//gi)].length }; } function pages({ html }) { const images = []; for (const match of html.matchAll(/]*class="[^"]*\bjs-page\b[^"]*"[^>]*>/gi)) { const url = match[0].match(/\bdata-src="([^"]+)"/i)?.[1]; if (url) images.push(url.replace(/&/g, '&')); } return { pages: [...new Set(images)] }; } module.exports = { catalog, pages };