142 lines
4.1 KiB
JavaScript
142 lines
4.1 KiB
JavaScript
class wattpad {
|
|
constructor() {
|
|
this.baseUrl = "https://wattpad.com";
|
|
this.type = "book-board";
|
|
this.mediaType = "ln";
|
|
}
|
|
|
|
async search(queryObj) {
|
|
const query = queryObj.query?.trim() || "";
|
|
const limit = 15;
|
|
const offset = 0;
|
|
|
|
const url =
|
|
`${this.baseUrl}/v4/search/stories?` +
|
|
`query=${encodeURIComponent(query)}` +
|
|
`&limit=${limit}&offset=${offset}&mature=false`;
|
|
|
|
const json = await fetch(url).then(r => r.json());
|
|
|
|
return json.stories.map(n => ({
|
|
id: n.id,
|
|
title: n.title,
|
|
image: n.cover,
|
|
sampleImageUrl: n.cover,
|
|
tags: n.tags,
|
|
type: "book"
|
|
}));
|
|
}
|
|
|
|
async getMetadata(id) {
|
|
const html = await fetch(`${this.baseUrl}/story/${id}`).then(r => r.text());
|
|
const $ = this.cheerio.load(html);
|
|
|
|
const script = $('script')
|
|
.map((_, el) => $(el).html())
|
|
.get()
|
|
.find(t => t?.includes('window.__remixContext'));
|
|
|
|
if (!script) return null;
|
|
|
|
const jsonText = script.match(/window\.__remixContext\s*=\s*({[\s\S]*?});/)?.[1];
|
|
if (!jsonText) return null;
|
|
|
|
let ctx;
|
|
try {
|
|
ctx = JSON.parse(jsonText);
|
|
} catch {
|
|
return null;
|
|
}
|
|
|
|
const route = ctx?.state?.loaderData?.["routes/story.$storyid"];
|
|
const story = route?.story;
|
|
const meta = route?.meta;
|
|
|
|
if (!story) return null;
|
|
|
|
return {
|
|
id: story.id,
|
|
title: story.title,
|
|
format: "Novel",
|
|
score: story.voteCount ?? null,
|
|
genres: story.tags || [],
|
|
status: story.completed ? "Completed" : "Ongoing",
|
|
published: story.createDate?.split("T")[0] || "???",
|
|
summary: story.description || meta?.description || "",
|
|
chapters: story.numParts || story.parts?.length || 1,
|
|
image: story.cover || meta?.image || "",
|
|
language: story.language?.name?.toLowerCase() || "unknown",
|
|
};
|
|
}
|
|
|
|
async findChapters(bookId) {
|
|
const html = await fetch(`${this.baseUrl}/story/${bookId}`).then(r => r.text());
|
|
const $ = this.cheerio.load(html);
|
|
|
|
const script = $('script')
|
|
.map((_, el) => $(el).html())
|
|
.get()
|
|
.find(t => t?.includes('window.__remixContext'));
|
|
|
|
if (!script) return [];
|
|
|
|
const jsonText = script.match(/window\.__remixContext\s*=\s*({[\s\S]*?});/)?.[1];
|
|
if (!jsonText) return [];
|
|
|
|
let ctx;
|
|
try {
|
|
ctx = JSON.parse(jsonText);
|
|
} catch {
|
|
return [];
|
|
}
|
|
|
|
const story = ctx?.state?.loaderData?.["routes/story.$storyid"]?.story;
|
|
if (!story?.parts) return [];
|
|
|
|
return story.parts.map((p, i) => ({
|
|
id: String(p.id),
|
|
title: p.title || `Chapter ${i + 1}`,
|
|
number: i + 1,
|
|
language: story.language?.name?.toLowerCase() || "en",
|
|
index: i
|
|
}));
|
|
}
|
|
|
|
async findChapterPages(chapterId) {
|
|
const html = await fetch(`https://www.wattpad.com/amp/${chapterId}`).then(r => r.text());
|
|
const $ = this.cheerio.load(html);
|
|
|
|
const title = $('h2').first().text().trim();
|
|
|
|
const container = $('.story-body-type');
|
|
if (!container.length) return "";
|
|
|
|
container.find('[data-media-type="image"]').remove();
|
|
|
|
const parts = [];
|
|
|
|
container.find('p').each((_, el) => {
|
|
const text = $(el)
|
|
.html()
|
|
.replace(/\u00A0/g, " ")
|
|
.replace(/[ \t]+/g, " ")
|
|
.trim();
|
|
|
|
if (text) parts.push(`<p>${text}</p>`);
|
|
});
|
|
|
|
container.find('amp-img').each((_, el) => {
|
|
const src = $(el).attr('src');
|
|
const w = $(el).attr('width');
|
|
const h = $(el).attr('height');
|
|
if (src) parts.push(`<img src="${src}" width="${w}" height="${h}">`);
|
|
});
|
|
|
|
return (
|
|
(title ? `<h1>${title}</h1>\n\n` : "") +
|
|
parts.join("\n\n")
|
|
).trim();
|
|
}
|
|
}
|
|
|
|
module.exports = wattpad; |