热点书城
https://m.rdrw8.com
zpccool (13551) 1天前 下载:380
小说 小说 免费
热点书城小说书源,基于m站。支持Cloudflare验证。
// @name 热点书城
// @version 1.0.0
// @uuid redianshucheng
// @author Ai
// @url https://m.rdrw8.com
// @type novel
// @enabled true
// @tags 小说,免费
// @description 热点书城小说书源,基于m站。支持Cloudflare验证。
var BASE = "https://m.rdrw8.com";
// ─── CF 保护检测 ────────────────────────────────────────────────────────
function isCfBlocked(html) {
if (!html || html.length < 200) {
legado.log("[CF] blocked: html too short");
return true;
}
var markers = [
"Just a moment",
"cf-browser-verification",
"Checking your browser",
"cf-challenge-running",
"managed_checking_msg",
"cf-please-wait",
"cf-turnstile-wrapper",
"正在进行安全验证",
];
for (var i = 0; i < markers.length; i++) {
if (html.indexOf(markers[i]) !== -1) {
legado.log("[CF] blocked: matched '" + markers[i] + "'");
return true;
}
}
return false;
}
// ─── CF 验证通过 ────────────────────────────────────────────────────────
async function ensureCfPassed(url, html) {
if (!isCfBlocked(html)) return html;
legado.log("[CF] 检测到CF拦截,启动浏览器探测...");
var sessionId = await legado.browser.acquire("cf", { visible: false });
legado.log("[CF] 会话 id=" + sessionId);
await legado.browser.navigate(sessionId, url, { waitFor: "load" });
var maxAttempts = 60;
var passed = false;
var realHtml = null;
var browserShown = false;
for (var i = 0; i < maxAttempts; i++) {
var pageHtml = await legado.browser.html(sessionId);
if (pageHtml && pageHtml.length > 500 && !isCfBlocked(pageHtml)) {
passed = true;
realHtml = pageHtml;
legado.log("[CF] 验证通过 (尝试 " + (i + 1) + " 次)");
break;
}
if (!browserShown) {
legado.toast("需要完成CF验证,请在弹出窗口中操作");
await legado.browser.show(sessionId);
browserShown = true;
}
legado.sleep(1000);
}
if (!passed) {
legado.log("[CF] 验证超时");
await legado.browser.hide(sessionId);
return html;
}
// 同步Cookie
await legado.browser.cookies(url);
var httpCookies = legado.http.cookies(url);
legado.log("[CF] Cookie已同步: " + httpCookies.substring(0, 100));
await legado.browser.hide(sessionId);
return realHtml;
}
// ─── 工具函数 ──────────────────────────────────────────────────────────
function toAbs(href) {
if (!href) return "";
if (href.indexOf("http") === 0) return href;
if (href.indexOf("//") === 0) return "https:" + href;
if (href.indexOf("/") === 0) return BASE + href;
return BASE + "/" + href;
}
// ─── 搜索 ──────────────────────────────────────────────────────────────
async function search(keyword, page) {
var url = BASE + "/s?q=" + encodeURIComponent(keyword);
legado.log("[search] keyword=" + keyword + " url=" + url);
var html = await legado.http.get(url);
html = await ensureCfPassed(url, html);
var doc = legado.dom.parse(html);
var books = [];
// 尝试多种列表选择器
var selectors = [
".bookitem", ".book-item", ".list-item", ".novel-item",
"article", "li", ".item"
];
var items = [];
for (var s = 0; s < selectors.length; s++) {
items = legado.dom.selectAll(doc, selectors[s]);
if (items && items.length > 0) break;
}
for (var i = 0; i < items.length; i++) {
var item = items[i];
var nameEl = legado.dom.select(item, "h3, h2, .title, .name, a");
var authorEl = legado.dom.select(item, ".author, .writer, .name");
var bookUrlEl = legado.dom.select(item, "a");
if (nameEl) {
var name = legado.dom.text(nameEl).replace(/[\r\n\t]/g, "").trim();
var author = authorEl ? legado.dom.text(authorEl).replace(/[\r\n\t]/g, "").trim() : "";
var bookUrl = bookUrlEl ? legado.dom.attr(bookUrlEl, "href") : "";
if (bookUrl && name && name.length > 0) {
books.push({
name: name,
author: author,
bookUrl: toAbs(bookUrl),
coverUrl: "",
kind: "",
lastChapter: ""
});
}
}
}
legado.dom.free(doc);
legado.log("[search] 返回 " + books.length + " 条结果");
return books;
}
// ─── 书籍详情 ─────────────────────────────────────────────────────────
async function bookInfo(bookUrl) {
legado.log("[bookInfo] url=" + bookUrl);
var html = await legado.http.get(bookUrl);
html = await ensureCfPassed(bookUrl, html);
var doc = legado.dom.parse(html);
var name = "";
var nameEl = legado.dom.select(doc, "h1, .book-title, .title, .bookname");
if (nameEl) {
name = legado.dom.text(nameEl).replace(/[\r\n\t]/g, "").trim();
}
var author = "";
var authorEl = legado.dom.select(doc, ".author, .writer, [class*='author'], [class*='writer']");
if (authorEl) {
author = legado.dom.text(authorEl).replace(/[\r\n\t]/g, "").trim();
}
var coverUrl = "";
var coverEl = legado.dom.select(doc, ".cover, .book-cover, img[class*='cover']");
if (coverEl) {
coverUrl = legado.dom.attr(coverEl, "src") || legado.dom.attr(coverEl, "data-src") || "";
coverUrl = toAbs(coverUrl);
}
var intro = "";
var introEl = legado.dom.select(doc, ".intro, .description, .book-intro, .summary");
if (introEl) {
intro = legado.dom.text(introEl).replace(/[\r\n\t]/g, "").trim();
}
// 查找目录链接
var tocUrl = "";
var tocEl = legado.dom.select(doc, "a[href*='chapter'], a[href*='catalogue'], a[href*='catalog'], .chapter-list a");
if (tocEl) {
tocUrl = legado.dom.attr(tocEl, "href");
if (tocUrl) tocUrl = toAbs(tocUrl);
}
if (!tocUrl) tocUrl = bookUrl;
legado.dom.free(doc);
legado.log("[bookInfo] name=" + name + " tocUrl=" + tocUrl);
return {
name: name,
author: author,
bookUrl: bookUrl,
coverUrl: coverUrl,
kind: "",
lastChapter: "",
intro: intro,
tocUrl: tocUrl
};
}
// ─── 章节列表 ─────────────────────────────────────────────────────────
async function chapterList(tocUrl) {
legado.log("[chapterList] url=" + tocUrl);
var html = await legado.http.get(tocUrl);
html = await ensureCfPassed(tocUrl, html);
var doc = legado.dom.parse(html);
var chapters = [];
var selectors = [
".chapter-list a", "#j_chapterList a", ".catalog-list a",
".chapter-ul a", "ul.chapter a", ".list-container a",
"dl.chapter a", ".chapterlist a", ".chapter-box a",
"ul li a", ".list li a"
];
var links = null;
for (var s = 0; s < selectors.length; s++) {
links = legado.dom.selectAll(doc, selectors[s]);
if (links && links.length > 0) {
legado.log("[chapterList] 使用选择器: " + selectors[s]);
break;
}
}
if (links) {
for (var i = 0; i < links.length; i++) {
var link = links[i];
var chapterName = legado.dom.text(link).replace(/[\r\n\t\s]/g, "").trim();
var chapterUrl = legado.dom.attr(link, "href");
if (chapterName && chapterUrl) {
chapters.push({
name: chapterName,
url: toAbs(chapterUrl)
});
}
}
}
legado.dom.free(doc);
legado.log("[chapterList] 返回 " + chapters.length + " 章");
return chapters;
}
// ─── 章节正文 ─────────────────────────────────────────────────────────
async function chapterContent(chapterUrl) {
legado.log("[chapterContent] url=" + chapterUrl);
var html = await legado.http.get(chapterUrl);
html = await ensureCfPassed(chapterUrl, html);
var doc = legado.dom.parse(html);
var contentSelectors = [
"#J_content", ".content-text", ".chapter-content", ".book-content",
"#chapter-content", ".article-content", ".read-content",
".text-content", ".novel-content", "#content", ".content"
];
var content = "";
for (var i = 0; i < contentSelectors.length; i++) {
var contentEl = legado.dom.select(doc, contentSelectors[i]);
if (contentEl) {
content = legado.dom.text(contentEl).replace(/[\r\n\t]/g, "\n").trim();
content = content.replace(/\n{3,}/g, "\n\n").trim();
legado.log("[chapterContent] 使用选择器: " + contentSelectors[i]);
break;
}
}
// 如果没找到,尝试获取body
if (!content) {
legado.dom.remove(doc, "script");
legado.dom.remove(doc, "style");
legado.dom.remove(doc, "nav");
legado.dom.remove(doc, "header");
legado.dom.remove(doc, "footer");
legado.dom.remove(doc, ".ad");
var body = legado.dom.select(doc, "body");
if (body) {
content = legado.dom.text(body).replace(/[\r\n\t]/g, "\n").trim();
content = content.replace(/\n{3,}/g, "\n\n").trim();
}
}
legado.dom.free(doc);
legado.log("[chapterContent] 正文长度: " + content.length);
return content;
}
// ─── 发现页 ──────────────────────────────────────────────────────────
async function explore(page, category) {
legado.log("[explore] page=" + page + " category=" + category);
var url = BASE + "/rank?page=" + page;
if (category) {
url = BASE + "/category/" + category + "?page=" + page;
}
var html = await legado.http.get(url);
html = await ensureCfPassed(url, html);
var doc = legado.dom.parse(html);
var books = [];
var selectors = [".bookitem", ".book-item", ".list-item", ".rank-item", "li", "article"];
var items = [];
for (var s = 0; s < selectors.length; s++) {
items = legado.dom.selectAll(doc, selectors[s]);
if (items && items.length > 0) break;
}
for (var i = 0; i < items.length; i++) {
var item = items[i];
var nameEl = legado.dom.select(item, "h3, h2, .title, .name, a");
var authorEl = legado.dom.select(item, ".author, .writer");
var bookUrlEl = legado.dom.select(item, "a");
if (nameEl) {
var name = legado.dom.text(nameEl).replace(/[\r\n\t]/g, "").trim();
var author = authorEl ? legado.dom.text(authorEl).replace(/[\r\n\t]/g, "").trim() : "";
var bookUrl = bookUrlEl ? legado.dom.attr(bookUrlEl, "href") : "";
if (bookUrl && name && name.length > 0) {
books.push({
name: name,
author: author,
bookUrl: toAbs(bookUrl),
coverUrl: "",
kind: "",
lastChapter: ""
});
}
}
}
legado.dom.free(doc);
legado.log("[explore] 返回 " + books.length + " 条");
return books;
}