热点书城

https://m.rdrw8.com

zpccool (13551) 1天前 下载:380

小说 小说 免费
热点书城小说书源,基于m站。支持Cloudflare验证。
二维码导入(APP尚未完成该功能)
// @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;
}
广告