小说自动翻页

可设置翻页数。已适配起点小说、小说狂人、自我小说、全本小说、17k小说、纵横小说。暂未适配:刺猬猫、番茄、69书吧、独阅读、百度读书、晋江、有毒、少年梦、不可能文学。陆续添加支持(咕咕咕)。

2025-08-04 يوللانغان نەشرى. ئەڭ يېڭى نەشرىنى كۆرۈش.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         小说自动翻页
// @version     1.0
// @description  可设置翻页数。已适配起点小说、小说狂人、自我小说、全本小说、17k小说、纵横小说。暂未适配:刺猬猫、番茄、69书吧、独阅读、百度读书、晋江、有毒、少年梦、不可能文学。陆续添加支持(咕咕咕)。
//
//
// @match        *://www.qidian.com/chapter/*
// @match        *://czbooks.net/n/*
// @match        *://www.uuzw.cc/*
// @match        *://www.quanben-xiaoshuo.com/*
// @match        *://www.quanben.io/n/*
// @match        *://www.17k.com/chapter/*
// @match        *://read.zongheng.com/chapter/*
//
//
//
//
// @match1        *://www.qidiantu.com/booklists/*
// @match1        *://fanqienovel.com/reader/*
// @match1        *://www.ciweimao.com/chapter/*
// @match1        *://www.69shuba.com/txt/*
// @match1        *://dushu.baidu.com/pc/reader?gid=*
// @match1        *://www.cddaoyue.cn/chapter/book_chapter_detail/*
// @match1        *://www.jjwxc.net/onebook.php?novelid=*
// @match1        *://*:1122/*
// @match1        *://github.com/*
// @match1        *://www.google.com/*
// @match1        *://www.youdubook.com/bookread
// @match1        *://www.shaoniandream.com/readchapter/*
// @match1        *://wenxue.bkneng.com/www/book/read/*
//
// @namespace    https://greatest.deepsurf.us/users/1171320
// @author       yzcjd
// @author 1      ChatGPT4辅助
// @grant        GM_registerMenuCommand
// @grant        GM_getValue
// @grant        GM_setValue
// @run-at       document-end
// @license MIT
// ==/UserScript==

(function () {
  'use strict';

  // ====== 配置项 ======
  const contentSelector = 'main, article, .content, .post, #content, .article, .article-content';
  const includeKeywords = ['下一页', '下页', '下一章', '后页', 'continue', 'next', 'more', '›', '>>'];
  const excludeKeywords = ['首页', '尾页', 'first', 'last', 'top', 'bottom', '1'];
  const defaultPreloadPages = 10;

  let preloadPages = GM_getValue('preloadCount', defaultPreloadPages);
  const loadedURLs = new Set();
  let isLoading = false;
  let lastChapterURL = null;
  let originalTitle = document.title;

  const queue = []; // 明确按顺序推进的队列

  // ====== 菜单 ======
  GM_registerMenuCommand('页数', async () => {
    const val = prompt('预加载页数(建议≤20):', preloadPages);
    const n = parseInt(val);
    if (n > 0 && n <= 100) {
      GM_setValue('preloadCount', n);
      alert('设置成功,刷新页面生效');
    } else {
      alert('超100!不会崩溃,但还是警告一下。');
    }
  });

  // ====== 工具函数 ======
  const sleep = ms => new Promise(res => setTimeout(res, ms));
  const rand = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
  const getContentContainer = () => document.querySelector(contentSelector);
  const getScrollRatio = () => (window.scrollY + window.innerHeight) / document.documentElement.scrollHeight;

  function isNextPageElement(el) {
    const text = (el.textContent || '').trim().toLowerCase();
    return includeKeywords.some(k => text.includes(k)) &&
           !excludeKeywords.some(k => text.includes(k));
  }

  function findNextPageUrl(doc) {
    const all = Array.from(doc.querySelectorAll('a'));
    for (const a of all) {
      if (a.href && isNextPageElement(a)) {
        return new URL(a.href, location.href).href;
      }
    }
    const match = doc.URL.match(/([?&\/\-_\.](page|p)?[=\/\-_]?)(\d{1,4})(?=[^\d]|$)/i);
    if (match) {
      const prefix = match[1];
      const pageNum = parseInt(match[3]);
      return doc.URL.replace(match[0], prefix + (pageNum + 1));
    }
    return null;
  }

  async function fetchAndInsertPage(url) {
    if (!url || loadedURLs.has(url)) return null;

    try {
      isLoading = true;
      console.log('[FETCH]', url);
      const res = await fetch(url, { credentials: 'include' });
      const html = await res.text();

      const doc = new DOMParser().parseFromString(html, 'text/html');
      const content = doc.querySelector(contentSelector);

      if (content) {
        const wrapper = document.createElement('div');
        wrapper.style.opacity = '0';
        wrapper.style.transition = 'opacity 0.6s';
        wrapper.appendChild(content.cloneNode(true));

        const container = getContentContainer();
        if (container) {
          container.appendChild(document.createElement('hr'));
          container.appendChild(wrapper);
          requestAnimationFrame(() => (wrapper.style.opacity = '1'));
        }

        loadedURLs.add(url);
        lastChapterURL = url;
        document.title = originalTitle;

        return findNextPageUrl(doc);
      } else {
        console.warn('[未找到正文]', url);
      }
    } catch (e) {
      console.warn('[加载失败]', url, e);
    } finally {
      isLoading = false;
    }
    return null;
  }

  // ====== 队列顺序推进加载 ======
  async function processQueue() {
    while (queue.length > 0 && queue.length <= preloadPages && !isLoading) {
      const next = queue[0]; // 只取第一个,保证顺序
      await sleep(rand(1000, 1200));
      const nextUrl = await fetchAndInsertPage(next);
      queue.shift(); // 当前加载完成后移除
      if (nextUrl && !loadedURLs.has(nextUrl)) {
        queue.push(nextUrl); // 按顺序推进
      }
    }
  }

  // ====== 滚动触发检查队列是否需要推进 ======
  function handleScroll() {
    if (getScrollRatio() > 0.9 && queue.length < preloadPages) {
      processQueue();
    }
  }

  // ====== 主程序入口 ======
  async function main() {
    const firstUrl = findNextPageUrl(document);
    if (firstUrl) queue.push(firstUrl);
    window.addEventListener('scroll', handleScroll);
    processQueue();

    window.addEventListener('beforeunload', () => {
      if (lastChapterURL) {
        localStorage.setItem('autopager:lastURL', lastChapterURL);
        console.log('[保存记录]', lastChapterURL);
      }
    });
  }

  main();
})();