Novel Download

小说下载,个人测试使用,主要是为了熟悉js的语法(测试更新)

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

Advertisement:

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

Advertisement:

// ==UserScript==
// @name         Novel Download
// @namespace    http://tampermonkey.net/
// @version      1.1.2
// @description  小说下载,个人测试使用,主要是为了熟悉js的语法(测试更新)
// @author       You
// @match        https://www.xbiquge.bz/book/*
// @match        https://hongxiue.com/*
// @match        https://hongxiuf.com/*
// @match        https://ixunshu.net/xs/*
// @match        https://www.493d.com/book/*
// @match        http://www.xuanshu.org/book/*
// @match        http://www.99xs.net/book/*
// @match        https://gongzicp.com/novel-*
// @match        https://www.99xs.net/book/*
// @match        https://www.zhenhunxiaoshuo.com/*
// @match        https://zuqus.cc/txt/*
// @match        https://www.jiqinw.com/*
// @match        https://www.52shuku.vip/*
// @match        https://www.xbanxia.cc/books/*
// @match        https://www.kaye-ge.com/index/*
// @match        https://www.220book.com/book/*
// @match        https://www.ryhy.net/article/*
// @match        https://www.wtksm.com/novel/*
// @match        https://www.ynfdkj.com/biquge/*
// @match        https://www.82xs.com/bqg/*
// @match        https://www.82xs.com/index/*
// @match        https://3tb4weatuybs.blog.fc2.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=ixunshu.net
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/full.js
// ==/UserScript==

//说明:
//1、如果某个网站的脚本不能生效了,可能是因为在补充新网站之后导致的不兼容,重新调整脚本即可
//2、对于某些网站,需要用户手动做一些操作:自动展开所有章节、手动多次批量下载
//3、需要解密、反爬的网站越来越多,这种js脚本爬虫的形式可能逐渐不适用
//4、可以捐赠

var g_chapterURLList = [];      //全部章节列表
var g_paragraphList = [];       //段落内容列表 [临时变量] 所有的段落和在一起就是一本书
var g_chapterList = [];         //章节内容列表
var g_bTestDownload = false;    //是否测试下载
var g_bTestGetChapter = false;  //是否测试获取章节
var g_iTestDownloadCnt = 1;     //测试下载章节数
var g_handleCnt = 0;            //已经处理的总数
var g_chapterPromises = [];     //批量处理队列
var g_iMaxPromiseCount = 5;     //批次处理总数
var g_needSleep = false;        //是否需要睡眠
var g_sleepTime = 3000;         //单次睡眠时间
var g_resmap = new Map();       //结果集,是一个过程量
var g_ctrlMap = new Map();      //控制类型的map
var g_replaceMap = new Map();   //用来进行替换的map
var g_proxysites = [];          //代理网站
var g_proxysiteUseIndex = 0;    //当前使用的代理网站
var g_startDownloadIndex = 0;   //开始下载的索引
var g_downloadCount = -1;       //下载的数量
var g_batchSleep = 3;           //批次处理之后的睡眠时间


//小说基础信息
const rule_isBookMainPage = 'rule_isBookMainPage';                                  //网址检测,判断是是否为书籍主页
const rule_appendDownloadBtn = 'rule_appendDownloadBtn';                            //添加下载按钮
const rule_novelSaveName = 'rule_novelSaveName';                                    //获取小说名称、作者名称

//章节列表
const rule_getChapterListMode = 'rule_getChapterList';                                  //获取章节列表的类型 新页面获取 or 当前页面获取
const rule_getChapterListFromCurPage = 'rule_getChapterListFromCurPage';                //从当前页获取章节列表
const rule_getChapterListPageUrl = 'rule_getChapterPageUrl';                            //新页面获取章节列表时,获取新页面的链接
const rule_getChapterListNextPage = 'rule_getNextChapterPage';                          //章节列表有多页时,获取下一页
const rule_getChapterListContainer = 'rule_getChapterListContainer';                    //获取章节列表的容器
const rule_getChapterListFromContainer = 'rule_getPartListFromCointer';                 //章节列表有多页时,获取其中一页的列表
const rule_getChapterListCustom = 'rule_getChapterListCustom';                          //自定义获取章节列表的方式

//获取章节
const rule_getChapterContentMode = 'rule_getChapterContent';                            //获取章节内容的类型 多页 or 一页
//fun_getChapterContenFromOnePage
const rule_getChapterTitle = 'rule_getChapterTitle';                                //获取某一章的标题
const rule_getChapterContentContainer = 'rule_getChapterContentContainer';          //获取章节内容主体容器
const rule_getChapterLinesFromContainer = 'rule_getChapterListContentFromContainer'; //从主体容器中获取所有的段落
//fun_getChapterContentFromOnePageWithJsonResponse
const rule_getChapterContentPageCustom = 'rule_getContentPage';                     //自定义请求获取章节内容的方式 (专门用来进行特殊处理的)
const rule_getChapterTitleFromJson = 'rule_getChapterTitleFromJson';                //获取某一章的标题
const rule_getChapterLinesFromJson = 'rule_getChapterContentFromJson';              //从主体容器中获取所有的段落
//fun_getChapterContenPageByPage(这种默认是不支持json形式的)
const rule_checkFirstChapterPage = 'rule_checkFirstChapterPage';                    //如果一章很多页,判断是否为第一页
const rule_getNextChapterPage = 'rule_getChapterNextPage';                          //一章内容有多页,获取下一页的链接
//fun_getChapterContentFromOnePageCustom 自定义模式,对于一些特殊规则的网站而言
const rule_getChapterContentFromOnePageCustomImpl = 'rule_getChapterContentFromOnePageCustomImpl';//完全放开


//其他配置
const rule_chapterContentDecoder = 'rule_chapterContentDecoder';                    //内容解码器
const rule_filterTxt = 'rule_filterTxt';                                            //文本过滤


//睡眠一段时间
function fun_sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}

//从内容中获取文本
function fun_getContentFromHTML($data)
{
    let rfun_getChapterContentContainer = g_ctrlMap.get(rule_getChapterContentContainer);
    let rfun_getChapterContentFromContainer = g_ctrlMap.get(rule_getChapterLinesFromContainer);
    let rfun_filterTxt = g_ctrlMap.get(rule_filterTxt);
    // 获取 id 为 "booktxt" 的 div 元素
    var $container = rfun_getChapterContentContainer($data);

    // 如果未找到对应的 div,则提示错误并返回空字符串
    if (!$container.length) {
        console.log("不能从当前页面获取到章节主体");
        throw new error('error 不能从当前页面获取到章节主体');
    }

    let lines = rfun_getChapterContentFromContainer($container);

    //文本合并,并且检测是否需要过滤
    let txt = lines.join('\n');
    if(rfun_filterTxt !== undefined)
    {
        txt = rfun_filterTxt(txt);
    }
    return txt;
}


//这里有个问题,这个框架实际上并没有被抽象出来,基本上还是得按需调整
// 获取每一章的内容 一页一页的获取
async function fun_getChapterContenPageByPage(url) {
    console.log("正在获取章节内容:" + url);
    let rfun_decoder = g_ctrlMap.get(rule_chapterContentDecoder);
    let rfun_getChapterTitle = g_ctrlMap.get(rule_getChapterTitle);
    let rfun_checkFirstChapterPage = g_ctrlMap.get(rule_checkFirstChapterPage);
    let rfun_getChapterNextPageUrl = g_ctrlMap.get(rule_getNextChapterPage);

    try {
        // 发送 HTTP 请求并等待响应
        const response = await fetch(url);
        $data = $();
        if(rfun_decoder !== undefined){
            const gbkData = await response.arrayBuffer();
            const decoder = rfun_decoder();
            const data = decoder.decode(gbkData);
            $data = $(data);
        }
        else{
            const data = await response.text();
            $data = $(data);
        }

        //定义一个临时变量,最终需要返回
        var paragraphList = [];

        //如果是第一页,就应该写入章节信息
        if(rfun_checkFirstChapterPage(url)){
            let chapterTitle = rfun_getChapterTitle($data);
            if(chapterTitle.length > 0)
            {
                paragraphList.push("");
                paragraphList.push(chapterTitle); //TODO 暂时不处理章节名称
                paragraphList.push("");
            }
        }

        // 获取当前页面的小说内容
        var content = fun_getContentFromHTML($data);

        // 将当前页面的小说内容存储到数组中
        paragraphList.push(content);

        let nexpagetUrl = rfun_getChapterNextPageUrl($data);

        if(nexpagetUrl.length > 0)
        {
            console.log("存在下一页,继续获取:", nexpagetUrl);
            var nextContentList = await fun_getChapterContenPageByPage(nexpagetUrl); // 使用 await 等待递归调用完成
            paragraphList = paragraphList.concat(nextContentList);
        }
        else{
            console.log("已到达最后一页,停止获取内容。");
        }

        return paragraphList;
    } catch (error) {
        console.error("请求失败:", error);
        g_needSleep = true;
    }
}


//从指定页面获取完整的一页数据
async function fun_getChapterContenFromOnePage(url) {

    let rfun_decoder = g_ctrlMap.get(rule_chapterContentDecoder);
    let rfun_getChapterTitle = g_ctrlMap.get(rule_getChapterTitle);
    console.log("正在获取章节内容:" + url);
    try {
        //请求
        const response = await fetch(url);
        $data = $();
        if(rfun_decoder !== undefined){
            const gbkData = await response.arrayBuffer();
            const decoder = rfun_decoder();
            const data = decoder.decode(gbkData);
            $data = $(data);
        }
        else{
            const data = await response.text();
            $data = $(data);
        }

        let paragraphList = [];

        //章节头部
        let chapterTitle = rfun_getChapterTitle($data);
        if (chapterTitle.length > 0)
        {
            paragraphList.push("");
            paragraphList.push(chapterTitle); //TODO 暂时不处理章节名称
            paragraphList.push("");
        }

        //获取主体内容
        var content = fun_getContentFromHTML($data);
        paragraphList.push(content);
        return paragraphList;
    } catch (error) {
        console.error("请求失败:", error);
        g_needSleep = true;
    }
}

//对于json的这种情况,已经不能做到通用了,每个网站的都不一样,甚是难受,如果只有一页的话,直接写一个custom类型的应该也可以的
async function fun_getChapterContentFromOnePageWithJsonResponse(url)
{
    let rfun_getChapterTitleFromJson = g_ctrlMap.get(rule_getChapterTitleFromJson);
    let rfun_getChapterContentFromJson = g_ctrlMap.get(rule_getChapterLinesFromJson);
    let rfun_getChapterContentPageCustom = g_ctrlMap.get(rule_getChapterContentPageCustom);
    try{
        let jsonObj;
        //请求
        if(rfun_getChapterContentPageCustom !== undefined)
        {
            jsonObj = await rfun_getChapterContentPageCustom(url);
        }
        else
        {
            const response = await fetch(url);
            // 检查response.ok是否为true,以确保请求成功
            if (!response.ok) {
                throw new Error('网络响应失败');
            }

            // 解析JSON数据
            jsonObj = await response.json();
        }


        let paragraphList = [];

        //章节头部
        let chapterTitle = rfun_getChapterTitleFromJson(jsonObj);
        if (chapterTitle.length > 0)
        {
            paragraphList.push("");
            paragraphList.push(chapterTitle); //TODO 暂时不处理章节名称
            paragraphList.push("");
        }

        //获取主体内容
        let content = rfun_getChapterContentFromJson(jsonObj);

        paragraphList.push(content);
        return paragraphList;
    } catch (error){
        console.error("请求失败:", error);
        g_needSleep = true;
    }
}

//自定义的从指定网页获取小说内容的函数,用户需要实现inner
//由于内部可
async function fun_getChapterContentFromOnePageCustom(url)
{
    let rfun_getChapterContentFromOnePageCustomImpl = g_ctrlMap.get(rule_getChapterContentFromOnePageCustomImpl);
    try{

        let paragraphList = [];
        paragraphList = await rfun_getChapterContentFromOnePageCustomImpl(url);
        return paragraphList;

    } catch (error){
        console.error("请求失败:", error);
        g_needSleep = true;
    }
}


async function fun_getChapterContentPromise(url)
{
    return new Promise(async (resolve, reject) => {
        try {
            let rfun_getChapterContent = g_ctrlMap.get(rule_getChapterContentMode);
            var contentList = await rfun_getChapterContent(url);
            const resultMap = new Map();
            resultMap.set(url, contentList);
            resolve(resultMap);
            g_handleCnt += 1;
            console.log("进度:"+g_handleCnt+"/"+g_chapterURLList.length);
        } catch (error) {
            reject(error);
        }
    });

}

//获取章节列表
async function fun_getChapterListPBP(url)
{
    let rfun_getChapterContainer = g_ctrlMap.get(rule_getChapterListContainer);
    let rfun_getPartListFromContainer = g_ctrlMap.get(rule_getChapterListFromContainer);
    let rfun_getNextChapterPage = g_ctrlMap.get(rule_getChapterListNextPage);

    console.log("正在获取章节列表: "+url);
    try {
        // 发送 HTTP 请求并等待响应
        const response = await fetch(url);
        const data = await response.text();
        const $data = $(data);


        // 找到章节链接所在的元素s
        const $chapterContainer = rfun_getChapterContainer($data);
        rfun_getPartListFromContainer($chapterContainer);


        //不需要获取下一页
        if(rfun_getNextChapterPage === undefined){
            return;
        }

        //直接获取下一页链接
        let nextPageUrl = rfun_getNextChapterPage($data);
        if(nextPageUrl.length > 0){
            if(url.includes(nextPageUrl))
            {
                // 输出章节列表
                console.log("所有章节链接获取完毕。下一页和当前页重合!!");
            } else {
                await fun_getChapterListPBP(nextPageUrl);
            }
        } else {
            // 输出章节列表
            console.log("所有章节链接获取完毕。");
        }
    } catch (error) {
        console.error("请求失败:", error);
    }
}

async function fun_getChapterListFromNewPage()
{
    let rfun_getChapterPageUrl =  g_ctrlMap.get(rule_getChapterListPageUrl);
    let chapterURL = rfun_getChapterPageUrl();
    if (chapterURL.length === 0) {
        console.log("无法获取到章节列表页");
        return;
    }

    await fun_getChapterListPBP(chapterURL);
}

async function fun_getChapterListCustom()
{
    let rfun_getChapterListCustomImpl = g_ctrlMap.get(rule_getChapterListCustom);
    await rfun_getChapterListCustomImpl();
}

//从当前页面获取所有的章节(这里这样写出来,只是为了让逻辑看上去清晰)
async function fun_getChapterListFromCurPage()
{
    let rfun_getChapterListFromCurPage = g_ctrlMap.get(rule_getChapterListFromCurPage);
    rfun_getChapterListFromCurPage();
}


//获取小说下载的名称
function fun_getNovelSaveName() {
    let iarr_noveSaveNamefuns = g_ctrlMap.get(rule_novelSaveName);
    var bookTitle = iarr_noveSaveNamefuns[0]();
    var author = iarr_noveSaveNamefuns[1]();
    var originalBookName = '《' + bookTitle + '》作者:' + author;
    var optimizedBookName = originalBookName.replace(/[!@#$%^&*()+\=\[\]{};':"\\|,.<>\/?]/g, 'x');

    return {
        originalBookName: bookTitle,
        author: author,
        optimizedBookName: optimizedBookName
    };
}

//并行的获取一批数据
async function fun_PromiseHandle(resmap)
{
    try {
        const resultArray = await Promise.all(g_chapterPromises);
        // console.log(resultArray);
        // 这里可以继续处理resultMap
        resultArray.forEach((tempMap) => {
            tempMap.forEach((value,key) => {
                resmap.set(key, value);
            });
        });
    } catch (error) {
        console.error('Error fetching chapter content:', error);
    }
    g_chapterPromises = [];
}

//遍历章节列表,逐步下载小说内容 //这里是可以调整的,使用Promise并发的进行请求
async function fun_downloadChapterUrlList(chapterList)
{
let bInterrupt = false;
    for (let i = 0; i < chapterList.length; i++)
    {
        let url = chapterList[i];
        let p = fun_getChapterContentPromise(url);
        g_chapterPromises.push(p);
        if(g_chapterPromises.length >=g_iMaxPromiseCount)
        {
            await fun_PromiseHandle(g_resmap);

            if(g_batchSleep > 0)
            {
                console.log("批处理结束,睡眠"+g_batchSleep+"秒...");
                await fun_sleep(g_sleepTime);
                console.log("睡眠结束!");
            }
        }

        if(g_needSleep)
        {
            console.log("过程中出现错误,睡眠"+ g_sleepTime +"秒...");
            await fun_sleep(g_sleepTime);
            g_needSleep = false;
            console.log("睡眠结束!");
        }

        if(g_bTestDownload && i>=(g_iTestDownloadCnt-1))
        {
            bInterrupt = true;
            break;
        }


    }

    //需要再执行一次,保证余下的
    await fun_PromiseHandle(g_resmap);

    let failedList = [];

    //如果中断直接退出执行
    if(bInterrupt)
    {
        g_chapterURLList.forEach((url)=>{
            const dataArray = g_resmap.get(url);
            if(dataArray === undefined)
                return;

            dataArray.forEach((d)=>{
                g_paragraphList.push(d);
            });
        });
        return failedList;
    }



    g_chapterURLList.forEach((url)=>{
        const dataArray = g_resmap.get(url);
        if(dataArray === undefined)
        {
            failedList.push(url);
            return;
        }
        dataArray.forEach((d)=>{
            g_paragraphList.push(d);
        });
    });


    if(failedList.length !=0)
        g_paragraphList = [];

    return failedList;
}

//下载小说
async function fun_downloadNovel()
{
    //清空存储容器
    g_chapterURLList = [];
    g_chapterList = [];
    g_paragraphList = [];
    g_resmap = new Map();
    g_handleCnt = 0;

    let g_bookHeader = [];
    console.log("正在下载小说...");


    if(!g_ctrlMap.has(rule_novelSaveName))
    {
        console.log("没有书籍保存的规则,无法下载");
        return;
    }
    //获取保存的文件名称
    let novelInfo = fun_getNovelSaveName();
    console.log("书籍名称:"+novelInfo.optimizedBookName);

    //插入下载信息
    g_bookHeader.push("书名:" + novelInfo.originalBookName);
    g_bookHeader.push("作者:" + novelInfo.author);
    g_bookHeader.push("地址:" + window.location.href);
    g_bookHeader.push("下载:雯饰太一");
    g_bookHeader.push("形式:网页插件");
    g_bookHeader.push("说明:数据为网页爬取而来,作者写作不易,请尊重正版原创");
    g_bookHeader.push("");
    g_bookHeader.push("");

    //获取章节列表
    //有两种情况
    //1、从当前页面获取章节列表(完整列表、循环获取)
    //2、从新页面获取章节列表(完整列表、循环获取)

    if(!g_ctrlMap.has(rule_getChapterListMode)){
        console.log("没有获取章节列表的规则,无法下载");
        return;
    }
    let rfun_getChapterList =  g_ctrlMap.get(rule_getChapterListMode);
    await rfun_getChapterList();

    if (g_chapterURLList.length == 0){
        console.log("章节列表为空,取消下载任务")
        return;
    }
    else{
        console.log("章节总数:\n"+g_chapterURLList.length);
    }

    if(g_bTestGetChapter)
    {
        console.log('当前为测试模式,不继续执行');
        return;
    }

    if(g_startDownloadIndex>0)
    {
        if(g_downloadCount == -1)
            g_chapterURLList = g_chapterURLList.slice(g_startDownloadIndex,g_chapterURLList.length);
        else
            g_chapterURLList = g_chapterURLList.slice(g_startDownloadIndex,g_startDownloadIndex+g_downloadCount);
        console.log('Start download from ' + g_startDownloadIndex);
        console.log("章节总数:\n"+g_chapterURLList.length);
    }

    failedList = g_chapterURLList;
    let iDownloadBatch = 1;
    while(failedList.length!=0)
    {
        console.log("当前下载批次:"+iDownloadBatch);
        failedList = await fun_downloadChapterUrlList(failedList);
        iDownloadBatch += 1;
    }

    //内容拼接
    let allContents = g_bookHeader.join('\n') + g_paragraphList.join('\n');

    // 计算内容大小
    let contentSizeKB = (new Blob([allContents])).size / 1024; // 转换为 KB
    let contentSizeMB = contentSizeKB / 1024; // 转换为 MB

    // 输出内容大小
    if (contentSizeMB >= 1) {
        console.log("内容大小:", contentSizeMB.toFixed(2) + " MB");
    } else {
        console.log("内容大小:", contentSizeKB.toFixed(2) + " KB");
    }

    //将内容下载为文件
    let blob = new Blob([allContents], { type: "text/plain;charset=utf-8" });
    saveAs(blob, novelInfo.optimizedBookName+".txt");
}

//插入下载按钮
function fun_insertDownloadBtn(ifun_appendfun) {
    // 设置按钮的id为'local_download_btn'
    if(ifun_appendfun === null)
    {
        return false;
    }
    return ifun_appendfun(newButton);
}

//判断是否为需要匹配的网址
function fun_checkWebset(url,regStr)
{
    let regex = new RegExp(regStr);
    return regex.test(url);
}

//[bool]ifun_isMainPage:是否需要二次判断,当前页面是否需要插入下载按钮
//[bool]ifun_appendfun:插入下载按钮的方式,不同的页面可能在前面或者后面进行插入
function fun_downloadConfig()
{
    let rfun_isBookMakePage = g_ctrlMap.get(rule_isBookMainPage);
    //step1 是否需要插入下载按钮
    if(rfun_isBookMakePage !== undefined){
        if(!rfun_isBookMakePage()){
            console.log("不是书籍主页,脚本不生效!");
            return;
        }
    }

    let rfun_appendDownloadBtn = g_ctrlMap.get(rule_appendDownloadBtn);

    //step2 插入下载按钮
    if(rfun_appendDownloadBtn === undefined){
        console.log("没有下载按钮插入规则,无法下载");
        return;
    }
    else{
        let newButton = $('<button id="local_download_btn">下载书籍</button>');
        if(!rfun_appendDownloadBtn(newButton))
        {
            console.log("无法插入下载按钮");
            return;
        }
    }

    //step3 绑定下载事件
    $('#local_download_btn').click(function() { // 使用按钮的id来绑定点击事件
        fun_downloadNovel();
    });
}

(function() {
    'use strict';

    // Your code here...
    let url = window.location.href;
    console.log(url);

    //下面匹配不同的网址

    //[新笔趣阁](https://www.xbiquge.bz/)
    if(fun_checkWebset(url,'https://www.xbiquge.bz/book/[0-9]*/')){
        //插入按钮
        g_ctrlMap.set(rule_appendDownloadBtn, function(newButton){
            $('.box_con').append(newButton);
            return true;
        });

        //获取书名
        g_ctrlMap.set(rule_novelSaveName,[
            function(){return $('meta[property="og:novel:book_name"]').attr('content');},
            function(){return $('meta[property="og:novel:author"]').attr('content');}
        ]);

        //获取章节列表
        g_ctrlMap.set(rule_getChapterListMode,fun_getChapterListFromCurPage);
        g_ctrlMap.set(rule_getChapterListFromCurPage,function(){
            let dtCnt = 0;
            $('#list dl').children().each(function() {
                var tagName = this.tagName.toLowerCase();
                if (tagName === 'dt') dtCnt+=1;
                if (dtCnt >= 2){
                    var url = $(this).find('a').attr('href');
                    if(url){
                        g_chapterURLList.push(url);
                    }
                }
            });
        });

        //获取每一章的内容
        g_ctrlMap.set(rule_getChapterContentMode,fun_getChapterContenFromOnePage);
        g_ctrlMap.set(rule_chapterContentDecoder,function(){return new TextDecoder("gbk");})
        g_ctrlMap.set(rule_getChapterTitle,function($data){return $data.find('.bookname h1:first').text();});
        g_ctrlMap.set(rule_getChapterContentContainer,function($data){return $data.find('#content');});
        g_ctrlMap.set(rule_getChapterLinesFromContainer,function($container){
            let lines = [];
            $container.contents().each(function() {
                // 检查当前节点是否是文本节点
                if (this.nodeType !== Node.TEXT_NODE) return;
                let txt = this.textContent.trim();
                if (txt !== "") {
                    lines.push(txt);
                }
            });
            return lines;
        });
    }
    //[爱寻书](https://ixunshu.net)
    else if(fun_checkWebset(url,'https://ixunshu.net/xs/[0-9]*')){
        //插入按钮
        g_ctrlMap.set(rule_appendDownloadBtn, function(newButton){
            $('.readbtn').append(newButton);
            return true;
        });

        //获取书名
        g_ctrlMap.set(rule_novelSaveName,[
            function(){return $('#info h1').text().trim();},
            function(){return $('#info p:contains("作者:")').text().trim().replace('作者:', '');}
        ]);

        //获取章节列表
        g_ctrlMap.set(rule_getChapterListMode,fun_getChapterListFromNewPage);
        g_ctrlMap.set(rule_getChapterListPageUrl,function(){
            return $($('a[rel="chapter"] dt:contains("点击查看全部章节目录")')[0]).parent().attr('href');
        });
        g_ctrlMap.set(rule_getChapterListContainer,function($data){ return $data.find('#content_1'); });
        g_ctrlMap.set(rule_getChapterListFromContainer,function($container){
            $container.find('a[rel="chapter"]').each(function() {
                let chapterLink = $(this).attr('href');
                g_chapterURLList.push(chapterLink);
            });
        });
        g_ctrlMap.set(rule_getChapterListNextPage,function($data){
            const $nextPageBtn = $data.find('.index-container-btn:contains("下一页")');
            if ($nextPageBtn.length) {
                return nextPageBtn.attr('href');
            }
            return "";
        });

        //获取每一章的内容
        g_ctrlMap.set(rule_getChapterContentMode,fun_getChapterContenPageByPage);
        g_ctrlMap.set(rule_getChapterTitle,function($data){
            let sixthChild = $data.find('.con_top').contents()[6];
            let chapterTitle = "";
            if (sixthChild.nodeType === Node.TEXT_NODE && sixthChild.nodeValue.length >= 3) {
                chapterTitle = sixthChild.nodeValue.substring(3);
            }
            return chapterTitle;
        });
        g_ctrlMap.set(rule_checkFirstChapterPage,function(url){return !(/page=/.test(url));})
        g_ctrlMap.set(rule_getNextChapterPage,function($data){
            const $nextPageBtn = $data.find('a[rel="prev"]:contains("下一页")');
            if($nextPageBtn.length)
            {
                return nextPageBtn.attr('href');
            }
            return '';
        });
        g_ctrlMap.set(rule_getChapterContentContainer,function($data){return $data.find('#booktxt');});
        g_ctrlMap.set(rule_getChapterLinesFromContainer,function($container){
            let lines = [];
            $container.find('p').each(function()
            {
                const $t = $(this);
                if($t.find('a').length !=0) return;
                let txt = $t.text().trim();
                if(txt === "") return;
                if(txt === ": ") return;
                lines.push(txt);
            });
            return lines;
        });
    }
    //[红袖招](https://hongxiue.com/) [红袖招](https://hongxiuf.com/)
    else if(fun_checkWebset(url,'https://hongxiuf.com/*') || fun_checkWebset(url,'https://hongxiue.com/*'))
    {
        //定义过滤的规则
        g_replaceMap = new Map([
            ['\uE290','操'],['\uE291','嫩'],['\uE292','扭'],['\uE293','揉'],['\uE294','硬'],['\uE295','奸'],['\uE296','吸'],['\uE297','处'],['\uE298','道'],['\uE299','毛'],['\uE29A','捅'],['\uE29B','催'],['\uE29C','身'],['\uE29D','捏'],['\uE29E','芭'],['\uE29F','股'],['\uE2A0','搞'],['\uE2A1','喘'],['\uE2A2','翻'],['\uE2A3','握'],['\uE2A5','入'],['\uE2A7','翘'],['\uE2A8','迷'],['\uE2A9','嘴'],['\uE2AA','扒'],['\uE2AB','摸'],['\uE2AC','抽'],['\uE2AD','耻'],['\uE2AE','裸'],['\uE2AF','弄'],['\uE2B0','臀'],['\uE2B1','腹'],['\uE2B2','鸡'],['\uE2B3','肉'],['\uE2B4','粗'],['\uE2B5','肤'],['\uE2B6','挺'],['\uE2B7','流'],['\uE2B8','淫'],['\uE2B9','唇'],['\uE2BA','下'],['\uE2BB','头'],['\uE2BC','插'],['\uE2BD','舔'],['\uE2BE','湿'],['\uE2BF','屄'],['\uE2C0','纤'],['\uE2C1','阴'],['\uE2C2','脚'],['\uE2C3','射'],['\uE2C4','推'],['\uE2C5','精'],['\uE2C6','媚'],['\uE2C7','咬'],['\uE2C8','舐'],['\uE2C9','乳'],['\uE2CA','干'],['\uE2CB','抚'],['\uE2CC','欲'],['\uE2CD','钻'],['\uE2CE','潮'],['\uE2CF','做'],['\uE2D0','骚'],['\uE2D1','体'],['\uE2D2','房'],['\uE2D3','掏'],['\uE2D4','满'],['\uE2D5','阳'],['\uE2D6','叉'],['\uE2D7','性'],['\uE2D8','裤'],['\uE2D9','拔'],['\uE2DA','光'],['\uE2DB','茎'],['\uE2DC','丰'],['\uE2DD','含'],['\uE2DE','根'],['\uE2DF','浪'],['\uE2E0','色'],['\uE2E1','胸'],['\uE2E2','龟'],['\uE2E3','药'],['\uE2E4','漏'],['\uE2E5','痒'],['\uE2E6','顶'],['\uE2E7','尿'],['\uE2E8','荡'],['\uE2E9','勃'],['\uE2EA','情'],['\uE2EB','贪'],['\uE2EC','诱'],['\uE2ED','沟'],['\uE2EE','吻'],['\uE2EF','腿'],['\uE2F0','爱'],['\uE2F1','坚'],['\uE2F3','液'],['\uE2F4','女'],['\uE2F5','屁'],['\uE2F6','席'],['\uE2F7','穴'],['\uE2F8','白'],['\uE2F9','趴'],['\uE2FA','奶'],['\uE2FB','撩'],['\uE2FC','罩'],['\uE2FD','裙'],['\uE2FE','滑'],['\uE2FF','软'],['\uE300','蜜'],['\uE301','柔'],['\uE302','搓'],['\uE303','吹'],['\uE304','尻'],['\uE305','爆'],['\uE306','交'],['\uE307','吮'],['\uE308','水'],['\uE309','脱'],['\uE30A','露'],['\uE30B','口'],['\uE30C','的'],['\uE30D','袜'],['\uE30E','呻'],['\uE30F','妇'],['\uE310','逗'],['\uE311','腰'],['\uE312','洞'],['\uE313','胀'],['\uE314','啊'],['\uE315','蒂'],['\uE316','户'],['\uE317','肥'],['\uE320','共'],['\uE321','党'],['\uE322','习'],['\uE323','产']
            ]);

        //判断是否为书籍主页
        g_ctrlMap.set(rule_isBookMainPage,function(){
            return ($('.inner .m-info .author').length >=0);
        });

        //插入按钮
        g_ctrlMap.set(rule_appendDownloadBtn, function(newButton){
            $('.ops').append(newButton);
            return true;
        });

        //获取书名
        g_ctrlMap.set(rule_novelSaveName,[
            function(){return $('.m-info > h1:first').text();},
            function(){return $('.m-info .author > a:first').text();}
        ]);

        //获取章节列表
        g_ctrlMap.set(rule_getChapterListMode,fun_getChapterListFromCurPage);
        g_ctrlMap.set(rule_getChapterListFromCurPage,function(){
            let dtCnt = 0;
            //第二个dt之后的所有内容全部都是
            $('.m-chapters a').each(function() {
                let chapterLink = $(this).attr('href');
                g_chapterURLList.push(chapterLink);
            });
        });

        //获取每一章的内容
        g_ctrlMap.set(rule_getChapterContentMode,fun_getChapterContenFromOnePage);
        g_ctrlMap.set(rule_getChapterTitle,function($data){return $data.find('.article-content h1:first').text();});
        g_ctrlMap.set(rule_getChapterContentContainer,function($data){return $data.find('.article-content');});
        g_ctrlMap.set(rule_getChapterLinesFromContainer,function($container){
            let lines = [];
            $container.find('p').each(function() {
                if ($(this).attr('style'))  return;
                if($(this).find('a').length != 0) return;
                let text = this.textContent.trim();
                lines.push(text);
            });
            return lines;
        });

        g_ctrlMap.set(rule_filterTxt,function(txt){
            g_replaceMap.forEach(function(value, key){
                txt = txt.replaceAll(key,value);
            });
            return txt;
        });
    }
    //[免费小说网](https://www.493d.com)
    else if(fun_checkWebset(url,'https://www.493d.com/book/[0-9]*.html')) {
        //插入按钮
        g_ctrlMap.set(rule_appendDownloadBtn,function(newButton){
            $('div._bts.pa.l0').append(newButton);
            return true;
        });

        //获取书名
        g_ctrlMap.set(rule_novelSaveName,[
            function(){return $('h1.mb15.lh1d2.oh').text();},
            function(){return $('p.mb15.ell._tags.pt2').find('span:first').text();}
        ]);

        //获取章节列表
        g_ctrlMap.set(rule_getChapterListMode,fun_getChapterListFromCurPage);
        g_ctrlMap.set(rule_getChapterListFromCurPage,function(){
            $('#chapterList li').each(function() {
                let chapterURL = $(this).find('a').attr('href');
                g_chapterURLList.push(chapterURL);
            });
        });

        //获取每一章的内容
        g_ctrlMap.set(rule_getChapterContentMode,fun_getChapterContenFromOnePage);
        g_ctrlMap.set(rule_getChapterTitle,function($data){return $data.find('#mlfy_main_text h1').text();});
        g_ctrlMap.set(rule_getChapterContentContainer,function($data){return $data.find('#TextContent');});
        g_ctrlMap.set(rule_getChapterLinesFromContainer,function($container){
            let lines = [];
            $container.contents().each(function() {
                if (this.nodeType === Node.TEXT_NODE) { // 判断节点类型是否为文本节点
                    let text = $(this).text().trim();
                    if (text !== '') {
                        lines.push(text);
                    }
                }
            });
            return lines;
        });
    }
    //[选书网](http://www.xuanshu.org)
    else if(fun_checkWebset(url,'http://www.xuanshu.org/book/[0-9]*/'))
    {
        //插入按钮
        g_ctrlMap.set(rule_appendDownloadBtn,function(newButton){
            $('div.info_des').append(newButton);
            return true;
        });

        //获取书名
        g_ctrlMap.set(rule_novelSaveName,[
            function(){return $('div.info_des h1').text();},
            function(){return $('div.info_des dl:first').text().match(/作.*者:(.+)/)[1].trim();}
        ]);

        //获取章节列表
        g_ctrlMap.set(rule_getChapterListMode,fun_getChapterListFromCurPage);
        g_ctrlMap.set(rule_getChapterListFromCurPage,function(){
            $('.pc_list li').each(function() {
                let chapterURL = $(this).find('a').attr('href');
                g_chapterURLList.push(chapterURL);
            });
        });

        //获取每一章的内容
        g_ctrlMap.set(rule_getChapterContentMode,fun_getChapterContenFromOnePage);
        g_ctrlMap.set(rule_getChapterTitle,function($data){return $data.find('.txt_cont h1:first').text();});
        g_ctrlMap.set(rule_getChapterContentContainer,function($data){return $data.find('#content1');});
        g_ctrlMap.set(rule_getChapterLinesFromContainer,function($container){
            let lines = [];
            $container.contents().each(function() {
                if (this.nodeType === Node.TEXT_NODE) { // 判断节点类型是否为文本节点
                    let text = $(this).text().trim();
                    if (text !== '') {
                        lines.push(text);
                    }
                }
            });
            return lines;
        });

    }
    //[久久小说](http://www.99xs.net/)
    else if(fun_checkWebset(url,'http://www.99xs.net/book/info[0-9]*/')){
        //插入按钮
        g_ctrlMap.set(rule_appendDownloadBtn,function(newButton){
            $('div.info').append(newButton);
            return true;
        });

        //获取书名
        g_ctrlMap.set(rule_novelSaveName,[
            function(){return $('div.top h1').text();},
            function(){return $('div.fix p:first').find('a').text();}
        ]);

        //获取章节列表
        g_ctrlMap.set(rule_getChapterListMode,fun_getChapterListFromNewPage);
        g_ctrlMap.set(rule_getChapterListPageUrl,function(){return window.location.href;});
        g_ctrlMap.set(rule_getChapterListContainer,function($data){ return $data.find('ul.section-list.fix:eq(1)'); });
        g_ctrlMap.set(rule_getChapterListFromContainer,function($container){
            $container.find('a').each(function() {
                let chapterLink = $(this).attr('href');
                g_chapterURLList.push(chapterLink);
            });
        });
        g_ctrlMap.set(rule_getChapterListNextPage,function($data){
            const $nextPageBtn = $data.find('div.index-container a:eq(1):contains("下一页")');
            if($nextPageBtn.length)
            {
                return $nextPageBtn.attr('href');
            }
            return "";
        });


        //获取每一章的内容
        g_ctrlMap.set(rule_getChapterContentMode,fun_getChapterContenPageByPage);
        g_ctrlMap.set(rule_getChapterTitle,function($data){
            let sixthChild = $data.find('div.layout-tit.xs-hidden').contents()[6];
            let chapterTitle = "";
            if (sixthChild.nodeType === Node.TEXT_NODE && sixthChild.nodeValue.length >= 3) {
                chapterTitle = sixthChild.nodeValue.substring(3).trim();
            }
            return chapterTitle;
        });
        g_ctrlMap.set(rule_checkFirstChapterPage,function(url){return !(/_\d+\.html$/.test(url));})
        g_ctrlMap.set(rule_getNextChapterPage,function($data){
            const $nextPageBtn = $data.find('div.section-opt.m-bottom-opt a#next_url:contains("下一页")');
            if($nextPageBtn.length)
            {
                return $nextPageBtn.attr('href');
            }
            return '';
        });
        g_ctrlMap.set(rule_getChapterContentContainer,function($data){return $data.find('div#content');});
        g_ctrlMap.set(rule_getChapterLinesFromContainer,function($container){
            let lines = [];
            $container.find('p').each(function()
            {
                const $t = $(this);
                let txt = $t.text().trim();
                if(!txt.length) return;
                lines.push(txt);
            });
            return lines;
        });
    }
    // 长佩文学网
    else if(fun_checkWebset(url,'https://gongzicp.com/novel-[0-9]*.html'))
    {

        //local config
        {
            g_bTestDownload = false;    //是否测试下载
            g_iTestDownloadCnt = 1;     //测试下载章节数
            g_iMaxPromiseCount = 1;     //批次处理总数

            g_proxysites.push('https://195.3.223.101');
            g_proxysites.push('https://95.214.53.28');
            g_proxysites.push('https://195.3.220.74');
            g_proxysites.push('https://51.159.107.240');
            g_proxysites.push('https://195.3.220.223');
            g_proxysites.push('https://185.16.38.230');
            g_proxysites.push('https://51.159.194.246');
            g_proxysites.push('https://51.159.194.214');
            g_proxysites.push('');//使用本机访问
            g_proxysiteUseIndex = 0;

            g_sleepTime = 5000;
            g_startDownloadIndex = 52;
            g_downloadCount = 7;
        }

        //插入按钮
        g_ctrlMap.set(rule_appendDownloadBtn,function(newButton){
            console.log($('div.read').first());
            $('div.read').first().after(newButton);
            console.log('需要手动点击下载全部,才能下载章节');
            return true;
        });

        //获取书名
        g_ctrlMap.set(rule_novelSaveName,[
            function(){return $('h3.name').text();},
            function(){return $('.cp-info__status').find('span').first().contents().filter(function() {
                return this.nodeType === 3; // Node.TEXT_NODE
            }).text();}
        ]);

        //获取章节列表
        g_ctrlMap.set(rule_getChapterListMode,fun_getChapterListFromCurPage);
        g_ctrlMap.set(rule_getChapterListFromCurPage,function(){
            $('.chapter-list').find('a').each(function() {
                let chapterURL = $(this).attr('href');
                //重新获取
                let match = chapterURL.match(/\d+/);
                if(match)
                {
                    let realurl = '/webapi/novel/chapterGetInfo?cid='+match+'&server=0';
                    g_chapterURLList.push(realurl);
                }
            });
        });

        //获取每一章的内容
        g_ctrlMap.set(rule_getChapterContentMode,fun_getChapterContentFromOnePageWithJsonResponse);
        //实际上这些代理的ip地址访问一次就不能访问了
        if(true){
            g_ctrlMap.set(rule_getChapterContentPageCustom,async function(url){
                let proxystr = g_proxysites[g_proxysiteUseIndex % g_proxysites.length];
                g_proxysiteUseIndex += 1;

                //对于长佩文学网而言,倒不如使用本机地址,隔一段时间获取一章的内容,如果失败了睡眠时间翻倍,失败多次就手动打断点停止吧
                //这个网站是在是太难搞了
                proxystr = '';

                let rurl = '';
                if(proxystr.length > 0)
                {
                    rurl = proxystr + url + '&__cpo=aHR0cHM6Ly9nb25nemljcC5jb20';
                    try
                    {
                        function makeRequest(url) {
                            return new Promise((resolve, reject) => {
                                GM_xmlhttpRequest({
                                    method: 'GET',
                                    url: url,
                                    headers: {
                                        'Content-Type': 'application/json'
                                    },
                                    onload: function(response) {
                                        // 检查响应状态
                                        if (response.status >= 200 && response.status < 300) {
                                            // 解析并返回响应数据
                                            resolve(JSON.parse(response.responseText));
                                        } else {
                                            reject(new Error('Request failed with status ' + response.status));
                                        }
                                    },
                                    onerror: function(error) {
                                        reject(new Error('Request failed: ' + error));
                                    }
                                });
                            });
                        }

                        const josndata = await makeRequest(rurl);
                        await fun_sleep(3000);
                        return josndata;
                    } catch (error) {
                        throw new Error(`Failed to fetch data from ${rurl}: ${error.message}`);
                    }
                }
                else
                {
                    rurl = url;
                    try{
                        const response = await fetch(url);
                        // 检查response.ok是否为true,以确保请求成功
                        if (!response.ok) {
                            throw new Error('网络响应失败');
                        }

                        // 解析JSON数据
                        const josndata = await response.json();
                        await fun_sleep(3000);
                        return josndata;
                    } catch (error) {
                        throw new Error(`Failed to fetch data from ${rurl}: ${error.message}`);
                    }
                }
            });
        }
        g_ctrlMap.set(rule_getChapterTitleFromJson,function(jsonObj){
            let titlestr = jsonObj.data.chapterInfo.name;
            console.log(titlestr);
            return titlestr;
        });
        g_ctrlMap.set(rule_getChapterLinesFromJson,function(jsonObj){

            class ldecoder {
                constructor(e, t) {
                    e += parseInt("165455", 14).toString(32),
                    this.iv = CryptoJS.enc.Utf8.parse("$h$b3!" + e),
                    t = atob(t) + parseInt("4d5a6c8", 14).toString(36),
                    this.key = CryptoJS.enc.Utf8.parse(t + "A")
                }
                encrypt(e) {
                    typeof e != "string" && (e = JSON.stringify(e));
                    const t = CryptoJS.enc.Utf8.parse(e);
                    return CryptoJS.AES.encrypt(t, this.key, {
                        mode: CryptoJS.mode.CBC,
                        padding: CryptoJS.pad.Pkcs7,
                        iv: this.iv
                    }).toString()
                }
                decrypt(e) {
                    const t = CryptoJS.AES.decrypt(e, this.key, {
                        mode: CryptoJS.mode.CBC,
                        padding: CryptoJS.pad.Pkcs7,
                        iv: this.iv
                    });
                    return CryptoJS.enc.Utf8.stringify(t).toString()
                }
            }

            let ddd = new ldecoder("iGzsYn","dTBMUnJidSRFbg==");
            let contentstr = ddd.decrypt(jsonObj.data.chapterInfo.content);

            const commaCount = (contentstr.match(/,/g) || []).length;
            const endCount = (contentstr.match(/。/g) || []).length;

            if((commaCount+ endCount)<5)
            {
                g_sleepTime *= 1.5;
                if(g_sleepTime > 30000)
                {
                    g_sleepTime = 30000;
                }
                throw new Error(`get error content.`);
            }
            console.log(contentstr);
            return contentstr;
        });

    }
    //镇魂小说
    else if(fun_checkWebset(url,'https://www.zhenhunxiaoshuo.com/*/'))
    {

        //local config
        {
            g_bTestDownload = false;    //是否测试下载
            g_iTestDownloadCnt = 1;     //测试下载章节数
            g_iMaxPromiseCount = 3;     //批次处理总数
            g_batchSleep = 3;
        }

        //插入按钮
        g_ctrlMap.set(rule_appendDownloadBtn,function(newButton){
            $('div.focusbox-text').first().after(newButton);
            return true;
        });

        //获取书名
        g_ctrlMap.set(rule_novelSaveName,[
            function(){return $('h1.focusbox-title').text();},
            function(){
                let authorstr = $('div.focusbox-text p:first').contents()[0].nodeValue;
                authorstr = authorstr.slice(5,authorstr.length);
                return authorstr;
            }
        ]);

        //获取章节列表
        g_ctrlMap.set(rule_getChapterListMode,fun_getChapterListFromCurPage);
        g_ctrlMap.set(rule_getChapterListFromCurPage,function(){
            let dtCnt = 0;
            //第二个dt之后的所有内容全部都是
            $('div.excerpts-wrapper a').each(function() {
                let chapterLink = $(this).attr('href');
                g_chapterURLList.push(chapterLink);
            });
        });

        //获取每一章的内容
        g_ctrlMap.set(rule_getChapterContentMode,fun_getChapterContenFromOnePage);
        g_ctrlMap.set(rule_getChapterTitle,function($data){return $data.find('.article-title').text();});
        g_ctrlMap.set(rule_getChapterContentContainer,function($data){return $data.find('.article-content');});
        g_ctrlMap.set(rule_getChapterLinesFromContainer,function($container){
            let lines = [];
            $container.find('p').each(function() {
                let text = this.textContent.trim();
                lines.push(text);
            });
            return lines;
        });

    }
    //足趣读书
    else if(fun_checkWebset(url,'https://zuqus.cc/txt/.*.html'))
    {
        //local config
        {
            g_bTestDownload = false;    //是否测试下载
            g_iTestDownloadCnt = 1;     //测试下载章节数
            g_iMaxPromiseCount = 3;     //批次处理总数
        }

        //插入按钮
        g_ctrlMap.set(rule_appendDownloadBtn,function(newButton){
            $('div#info').after(newButton);
            return true;
        });

        //获取书名
        g_ctrlMap.set(rule_novelSaveName,[
            function(){return $('meta[property="og:novel:book_name"]').attr('content');},
            function(){return $('meta[property="og:novel:author"]').attr('content');}
        ]);

        //获取章节列表
        g_ctrlMap.set(rule_getChapterListMode,fun_getChapterListFromNewPage);
        g_ctrlMap.set(rule_getChapterListPageUrl,function(){
            let previousPageLink = $('div.pages a:contains("上页")').attr('href');
            if (previousPageLink) {
                console.log('章节页链接:', previousPageLink);
                return previousPageLink;
            } else {
                console.log('无法获取章节页')
                return '';
            }
        });
        g_ctrlMap.set(rule_getChapterListContainer,function($data){ return $data.find('div#list dl').first(); });
        g_ctrlMap.set(rule_getChapterListFromContainer,function($container){
            var isTextContentSection = false;
            $container.children().each(function() {
                if ($(this).is('dt') && $(this).text().includes('正文')) {
                    isTextContentSection = true;  // 找到包含“正文”的dt标签,开始记录后续的dd标签
                } else if ($(this).is('dt')) {
                    isTextContentSection = false; // 遇到下一个dt标签,停止记录
                }

                if (isTextContentSection && $(this).is('dd')) {
                    $(this).find('a').each(function() {
                        g_chapterURLList.push($(this).attr('href'));  // 提取dd标签中a标签的href属性
                    });
                }
            });
        });
        g_ctrlMap.set(rule_getChapterListNextPage,function($data){
            const $nextPageBtn = $data.find('div.pages a:contains("下页")');
            if($nextPageBtn.length)
            {
                let nextPageLink = $nextPageBtn.attr('href');
                return nextPageLink;
            }
            return "";
        });


        //获取每一章的内容
        g_ctrlMap.set(rule_getChapterContentMode,fun_getChapterContentFromOnePageCustom);
        g_ctrlMap.set(rule_getChapterContentFromOnePageCustomImpl,async function(url){
            try{
                let paragraphList = [];
                //请求
                const response = await fetch(url);
                const responseText = await response.text();

                let titlestr = '';
                let realgetlink = '';

                //情况1,直接返回的事script脚本的形式
                {
                    // 使用正则表达式提取 CT 和 CU
                    let ctMatch = responseText.match(/CT:\s*"([^"]+)"/);
                    let cuMatch = responseText.match(/CU:\s*"([^"]+)"/);



                    if (ctMatch && cuMatch) {
                        titlestr = ctMatch[1];
                        realgetlink = "https:" + cuMatch[1];
                    }
                }

                //情况2 半网页的形式
                {
                    // 定义正则表达式匹配initTxt函数调用的模式
                    let regex = /initTxt\("([^"]+)",\s*"([^"]+)"\)/;

                    // 执行正则表达式匹配
                    let match = regex.exec(responseText);

                    if (match) {
                        // match[1] 匹配到的是链接
                        realgetlink = "https:" + match[1];
                        // match[2] 匹配到的是章节名称
                        titlestr = match[2];
                    }


                }

                // 判断这两个变量是否为空,为空则抛出异常
                if (!titlestr) {
                    throw new Error(' (标题) 为空');
                }

                if (!realgetlink) {
                    throw new Error(' (链接) 为空');
                }

                console.log('Title:', titlestr);
                console.log('URL:', realgetlink);

                paragraphList.push("");
                paragraphList.push(titlestr);
                paragraphList.push("");

                function makeRequest(rlink) {
                    return new Promise((resolve, reject) => {
                        GM_xmlhttpRequest({
                            method: "GET",
                            url: rlink,
                            onload: function(response) {
                                resolve(response.responseText);
                            },
                            onerror: function(error) {
                                reject(error);
                            }
                        });
                    });
                }

                const unicodeContect = await makeRequest(realgetlink);

                //paragraphList.push(unicodeContect);

                function processText(text) {
                    const regex = /"([^"]*)"/g;
                    const matches = [];
                    let match;

                    while ((match = regex.exec(text)) !== null) {
                        matches.push(match[1]);
                    }

                    if(matches.length < 3)
                        return '';

                    let contentstr = matches[1];
                    for(let i = 3;i<matches.length;i+=2)
                    {
                        let k = matches[i+1];
                        let v = matches[i];

                        if(k==='\\b')
                        {
                            k='\\\\b';
                        }


                        contentstr = contentstr.replace(new RegExp(k, 'g'), v);
                    }
                    contentstr = contentstr.replace(/\\n/g, "\r\n");
                    return contentstr;
                }

                function decodeUnicode(encodedText) {
                    // 将编码文本中的 Unicode 转换为明文
                    const decodedText = encodedText.replace(/&#x([0-9A-Fa-f]+);/g, (match, hex) => {
                        return String.fromCharCode(parseInt(hex, 16));
                    });

                    return decodedText;
                }
                let contentstr = processText(unicodeContect);
                contentstr = decodeUnicode(contentstr);
                paragraphList.push(contentstr);
                return paragraphList;
            }  catch (error) {
                console.error("请求失败:", error);
                g_needSleep = true;
            }
        });
    }
    //腐小说
    else if(fun_checkWebset(url,'https:\/\/www\\.jiqinw\\.com\\/[^\\/]+\\/[0-9]+\\.html'))
    {
        console.log('匹配到腐小说网站');
        {
            g_bTestDownload = false;    //是否测试下载
            g_iTestDownloadCnt = 1;     //测试下载章节数
            g_iMaxPromiseCount = 3;     //批次处理总数
            g_batchSleep = 3;
            g_bTestGetChapter = false;
        }

        //插入按钮
        g_ctrlMap.set(rule_appendDownloadBtn,function(newButton){
            if($(".af_lst").length!=0)
            {
                console.log($(".af_lst"));
                $(".af_lst").append(newButton);
                return true;
            }
            else if($(".tits").length!=0)
            {
                $(".tits").append(newButton);
                return true;
            }
        });

        //获取书名
        g_ctrlMap.set(rule_novelSaveName,[
            function(){
                if($('meta[property="og:novel:book_name"]').length!=0)
                {
                    return $('meta[property="og:novel:book_name"]').attr('content');
                }
                else if($(".af_lst").length!=0)
                {
                    let bookTitle = $(".coa-an h1").text();
                    console.log("书名:", bookTitle);
                    return bookTitle;
                }
            },
            function(){
                if($('meta[property="og:novel:author"]').length!=0)
                {
                    return  $('meta[property="og:novel:author"]').attr('content');
                }
                else if($(".af_lst").length!=0)
                {
                    let authorName = $(".af_lst strong a").text();
                    console.log("作者名称:", authorName);
                    return authorName;
                }
            }
        ]);

        //获取章节列表
        g_ctrlMap.set(rule_getChapterListMode,fun_getChapterListFromCurPage);
        g_ctrlMap.set(rule_getChapterListFromCurPage,function(){
            // 提取总页数
            let totalPagesText = $(".alt_page li:first-child a").text();
            let totalPages = totalPagesText.match(/共(\d+)页/);

            if (totalPages) {
                totalPages = parseInt(totalPages[1], 10); // 转换为整数
                let baseUrl = window.location.href.replace('.html',''); // 请替换为实际的基本 URL

                // 生成链接
                for (let i = 1; i <= totalPages; i++) {
                    let url = i === 1 ? `${baseUrl}.html` : `${baseUrl}_${i}.html`;
                    g_chapterURLList.push(url);
                    // 如果你想将链接添加到页面,可以使用下面的代码
                    // $("#linksContainer").append(`<a href="${url}">${url}</a><br>`);
                }
            } else {
                console.log("未能提取总页数");
            }

            console.log(g_chapterURLList);
        });

        //获取每一章的内容
        g_ctrlMap.set(rule_getChapterContentMode,fun_getChapterContenFromOnePage);
        g_ctrlMap.set(rule_getChapterTitle,function($data){return '';});
        g_ctrlMap.set(rule_getChapterContentContainer,function($data){
            if($data.find('.wznrb').length!=0)
            {
                return $data.find('.wznrb');
            }
            else if($data.find('.co-bay').length!=0)
            {
                return $data.find('.co-bay');
            }
        });
        g_ctrlMap.set(rule_getChapterLinesFromContainer,function($container){
            let lines = [];
            let text = $container.text();
            // console.log(text);
            lines.push(text);
            return lines;
        });
    }
    //52书库
    else if(fun_checkWebset(url,'https://www.52shuku.vip/.*.html'))
    {
        //local config
        {
            g_bTestDownload = false;    //是否测试下载
            g_bTestGetChapter = false;
            g_iTestDownloadCnt = 1;     //测试下载章节数
            g_iMaxPromiseCount = 3;     //批次处理总数
        }

        //插入按钮
        g_ctrlMap.set(rule_appendDownloadBtn,function(newButton){
            let d = $('header.article-header div.meta');
            if (d.length === 0)  // 检查元素是否存在
                return false;
            d.append(newButton);
            return true;
        });

        //获取书名
        g_ctrlMap.set(rule_novelSaveName,[
            function(){
                const regex = /^(.*?)_(.*?)(?:\s*【.*?】)?$/;
                let str = $('h1.article-title').text();
                const match = str.match(regex);
                if(match)
                    return match[1].trim();
                else
                    return str;
            },
            function(){
                const regex = /^(.*?)_(.*?)(?:\s*【.*?】)?$/;
                let str = $('h1.article-title').text();
                const match = str.match(regex);
                if(match)
                    return match[2].trim();
                else
                    return '';
            }
        ]);

        //获取章节列表
        g_ctrlMap.set(rule_getChapterListMode,fun_getChapterListFromCurPage);
        g_ctrlMap.set(rule_getChapterListFromCurPage,function(){
            $('ul.list.clearfix li.mulu').each(function() {
                let chapterURL = $(this).find('a').attr('href');
                g_chapterURLList.push(chapterURL);
            });
        });

        //获取每一章的内容
        g_ctrlMap.set(rule_getChapterContentMode,fun_getChapterContenFromOnePage);
        g_ctrlMap.set(rule_getChapterTitle,function($data){return '';});
        g_ctrlMap.set(rule_getChapterContentContainer,function($data){return $data.find('.article-content');});
        g_ctrlMap.set(rule_getChapterLinesFromContainer,function($container){
            let lines = [];
            $container.find('p').each(function()
            {
                const $t = $(this);
                let txt = $t.text().trim();
                if(!txt.length) return;
                lines.push(txt);
            });
            return lines;
        });
    }
    //半夏小说
    else if(fun_checkWebset(url,'https://www.xbanxia.cc/books/.*.html'))
    {
        //local config
        {
            g_bTestDownload = false;    //是否测试下载
            g_bTestGetChapter = false;
            g_iTestDownloadCnt = 1;     //测试下载章节数
            g_iMaxPromiseCount = 3;     //批次处理总数
            g_batchSleep = 3;
        }

        //插入按钮
        g_ctrlMap.set(rule_appendDownloadBtn,function(newButton){
            // 在章节列表容器旁边插入下载按钮
            let chapterListContainer = $('div.book-list.clearfix');
            if (chapterListContainer.length === 0) {
                // 如果找不到章节列表容器,尝试在内容列表容器前插入
                chapterListContainer = $('div#content-list');
                if (chapterListContainer.length === 0) {
                    return false;
                }
                chapterListContainer.before(newButton);
            } else {
                // 在章节列表容器前插入下载按钮
                chapterListContainer.before(newButton);
            }
            return true;
        });

        //获取书名
        g_ctrlMap.set(rule_novelSaveName,[
            function(){
                // 直接从书籍描述区域的h1标签获取书名
                let bookTitleElement = $('div.  -describe h1');
                if (bookTitleElement.length !== 0)
                {
                    return bookTitleElement.text().trim();
                }

                // 备用方案:从页面标题获取书名
                if($('title').length !== 0)
                {
                    let title = $('title').text();
                    // 移除"小说全文在线阅读 - 半夏小说"部分
                    let bookTitle = title.replace(/小說全文在線閱讀 - 半夏小說$/, '').trim();

                    // 检查书名是否重复(如"Kiss Me if You Canx Kiss Me if You Can")
                    if (bookTitle.includes('x ')) {
                        // 分割并取第一部分
                        let parts = bookTitle.split('x ');
                        if (parts.length > 1) {
                            bookTitle = parts[0].trim();
                        }
                    }

                    return bookTitle;
                }
                return '';
            },
            function(){
                // 从作者信息段落获取作者
                let authorElement = $('div.book-describe p:contains("作者")');
                if (authorElement.length !== 0)
                {
                    let authorText = authorElement.text().trim();
                    // 提取作者名称(移除"作者︰"前缀)
                    let authorMatch = authorText.match(/作者︰(.+)/);
                    if (authorMatch && authorMatch[1]) {
                        return authorMatch[1].trim();
                    }
                }
                return '';
            }
        ]);

        //获取章节列表
        g_ctrlMap.set(rule_getChapterListMode,fun_getChapterListFromCurPage);
        g_ctrlMap.set(rule_getChapterListFromCurPage,function(){
            // 从书籍列表容器获取章节链接
            $('div.book-list.clearfix li a').each(function() {
                let href = $(this).attr('href');
                if (href && href.includes('/books/') && href.includes('.html')) {
                    g_chapterURLList.push(href);
                }
            });

            console.log('半夏小说章节列表:', g_chapterURLList);
        });

        //获取每一章的内容
        g_ctrlMap.set(rule_getChapterContentMode,fun_getChapterContenFromOnePage);
        g_ctrlMap.set(rule_getChapterTitle,function($data){
            // 获取章节标题
            let title = $data.find('h1#nr_title').text().trim();
            if (!title) {
                title = $data.find('h1.post-title').text().trim();
            }
            return title;
        });
        g_ctrlMap.set(rule_getChapterContentContainer,function($data){
            // 获取章节内容容器
            let container = $data.find('div#nr1');
            if (!container.length) {
                container = $data.find('div.post-content');
            }
            return container;
        });
        g_ctrlMap.set(rule_getChapterLinesFromContainer,function($container){
            let lines = [];
            // 获取所有段落内容,按行处理
            $container.find('p, div').each(function() {
                let $element = $(this);
                let text = $element.text().trim();
                if (text.length > 0) {
                    // 按换行符分割内容
                    let textLines = text.split(/\r?\n/);
                    textLines.forEach(function(line) {
                        let trimmedLine = line.trim();
                        if (trimmedLine.length > 0) {
                            lines.push(trimmedLine);
                        }
                    });
                }
            });

            // 如果没有找到段落内容,使用text()方法获取所有文本
            if (lines.length === 0) {
                let content = $container.text();
                if (content) {
                    let textLines = content.split(/\r?\n/);
                    textLines.forEach(function(line) {
                        let trimmedLine = line.trim();
                        if (trimmedLine.length > 0) {
                            lines.push(trimmedLine);
                        }
                    });
                }
            }

            return lines;
        });
    }
    //[卡夜阁](https://www.kaye-ge.com/)
    else if(fun_checkWebset(url,'https://www.kaye-ge.com/index/[0-9]*/'))
    {
        console.log("xxxxxxxxxxxxxxxxx");

        //local config
        {
            g_bTestDownload = false;    //是否测试下载
            g_bTestGetChapter = false;
            g_iTestDownloadCnt = 1;     //测试下载章节数
            g_iMaxPromiseCount = 3;     //批次处理总数
            g_batchSleep = 3;
        }


        //判断是否为书籍主页
        g_ctrlMap.set(rule_isBookMainPage,function(){
            return ($('.info').length > 0 && $('.section-list.fix').length > 0);
        });

        //插入按钮
        g_ctrlMap.set(rule_appendDownloadBtn,function(newButton){
            console.log('insert download button');
            $('.info').append(newButton);
            return true;
        });

        //获取书名
        g_ctrlMap.set(rule_novelSaveName,[
            function(){
                //优先从meta标签获取书名
                let metaBookName = $('meta[property="og:novel:book_name"]').attr('content');
                if(metaBookName && metaBookName.trim()) {
                    return metaBookName.trim();
                }
                //备用方案:从页面标题获取
                return $('title').text().trim();
            },
            function(){
                //从meta标签获取作者
                let metaAuthor = $('meta[property="og:novel:author"]').attr('content');
                if(metaAuthor && metaAuthor.trim()) {
                    return metaAuthor.trim();
                }
                //备用方案:从页面结构获取作者
                let authorText = $('.info p:contains("作者")').text();
                let match = authorText.match(/作者[:︰](.+)/);
                if(match && match[1]) {
                    return match[1].trim();
                }
                return '未知作者';
            }
        ]);

        //获取章节列表(分页模式)
        g_ctrlMap.set(rule_getChapterListMode,fun_getChapterListFromNewPage);
        g_ctrlMap.set(rule_getChapterListPageUrl,function(){
            return window.location.href;
        });
        g_ctrlMap.set(rule_getChapterListContainer,function($data){
            return $data.find('ul.section-list.fix:eq(1)');
        });
        g_ctrlMap.set(rule_getChapterListFromContainer,function($container){
            $container.find('a').each(function() {
                let chapterLink = $(this).attr('href');
                g_chapterURLList.push(chapterLink);
            });
        });
        g_ctrlMap.set(rule_getChapterListNextPage,function($data){
            const $nextPageBtn = $data.find('.index-container a:contains("下一页")');
            if($nextPageBtn.length && !$nextPageBtn.hasClass('disabled-btn'))
            {
                return $nextPageBtn.attr('href');
            }
            return "";
        });

        //获取每一章的内容(分页模式)
        g_ctrlMap.set(rule_getChapterContentMode,fun_getChapterContenPageByPage);
        g_ctrlMap.set(rule_getChapterTitle,function($data){
            return $data.find('h1.title').text().trim();
        });
        g_ctrlMap.set(rule_checkFirstChapterPage,function(url){
            return !(/_[0-9]+\.html$/.test(url));
        });
        g_ctrlMap.set(rule_getNextChapterPage,function($data){
            const $nextPageBtn = $data.find('a#next_url:contains("下一页")');
            if($nextPageBtn.length)
            {
                return $nextPageBtn.attr('href');
            }
            return '';
        });
        g_ctrlMap.set(rule_getChapterContentContainer,function($data){
            return $data.find('div#content');
        });
        g_ctrlMap.set(rule_getChapterLinesFromContainer,function($container){
            let lines = [];
            $container.find('p').each(function()
            {
                const $t = $(this);
                let txt = $t.text().trim();
                if(!txt.length) return;
                lines.push(txt);
            });
            return lines;
        });
    }
    //[顶点小说](https://www.220book.com/book/)
    else if(fun_checkWebset(url,'https://www.220book.com/book/[0-9A-Z]+/'))
    {
        console.log("顶点小说配置已激活");

        //local config
        {
            g_bTestDownload = false;    //是否测试下载
            g_bTestGetChapter = false;  //是否测试获取章节
            g_iTestDownloadCnt = 1;     //测试下载章节数
            g_iMaxPromiseCount = 3;     //批次处理总数
            g_batchSleep = 3;
        }

        //判断是否为书籍主页
        g_ctrlMap.set(rule_isBookMainPage,function(){
            return ($('div.nameW').length > 0 && $('div.btnW').length > 0);
        });

        //插入按钮
        g_ctrlMap.set(rule_appendDownloadBtn,function(newButton){
            console.log('在顶点小说页面插入下载按钮');
            $('div.chapterList').prepend(newButton);
            return true;
        });

        //获取书名
        g_ctrlMap.set(rule_novelSaveName,[
            function(){
                //优先从div.nameW中提取书名
                let bookName = $('div.nameW span.name.font28').text().trim();
                if(bookName && bookName.trim()) {
                    return bookName.trim();
                }
                //备用方案:从meta标签获取书名
                let metaBookName = $('meta[property="og:novel:book_name"]').attr('content');
                if(metaBookName && metaBookName.trim()) {
                    return metaBookName.trim();
                }
                //最后备用方案:从页面标题获取
                let titleText = $('title').text().trim();
                let match = titleText.match(/(.+?)_顶点小说/);
                if(match && match[1]) {
                    return match[1].trim();
                }
                return titleText;
            },
            function(){
                //优先从div.nameW中提取作者
                let authorName = $('div.nameW span.author.font18 a').text().trim();
                if(authorName && authorName.trim()) {
                    return authorName.trim();
                }
                //备用方案:从span.author中提取作者
                let authorText = $('div.nameW span.author.font18').text().trim();
                let match = authorText.match(/作者[:︰](.+)/);
                if(match && match[1]) {
                    return match[1].trim();
                }
                //最后备用方案:从meta标签获取作者
                let metaAuthor = $('meta[property="og:novel:author"]').attr('content');
                if(metaAuthor && metaAuthor.trim()) {
                    return metaAuthor.trim();
                }
                return '未知作者';
            }
        ]);

        //获取章节列表(分页模式)
        g_ctrlMap.set(rule_getChapterListMode,fun_getChapterListCustom);
        g_ctrlMap.set(rule_getChapterListCustom,async function(){
            // 1. 获取"查看全部章节"链接
            const allChaptersLink = $('div.allW a.all').attr('href');
            if(!allChaptersLink) {
                console.error('未找到"查看全部章节"链接');
                return;
            }

            // 获取全部章节的内容
            const allChaptersResponse = await fetch(allChaptersLink);
            const allChaptersHtml = await allChaptersResponse.text();
            const $allChaptersDoc = $(allChaptersHtml);

            // 获取当前页面的章节总数和分页信息
            const $pageSelect = $allChaptersDoc.find('select.select');
            // 目前不清楚单页的时候有什么不同的显示规则,先测试多页的
            if(!$pageSelect.length) {
                console.log('未找到章节分页下拉框,使用单页模式');
                // 单页模式:直接获取当前页面的章节列表
                $allChaptersDoc.find('div.list.list3.chapListBody ul li a').each(function() {
                    let chapterLink = $(this).attr('href');
                    if(chapterLink && chapterLink.includes('/book/')) {
                        g_chapterURLList.push(chapterLink);
                    }
                });
                return;
            }

            // 多页模式:通过POST请求获取所有分页的章节列表
            const totalPages = $pageSelect.find('option').length;

            // 从URL中提取书籍ID(如7SBR)
            const urlMatch = window.location.href.match(/\/book\/([0-9A-Z]+)/);
            const bookId = urlMatch ? urlMatch[1] : '';

            if(!bookId) {
                console.error('无法从URL中提取书籍ID');
                return;
            }

            console.log(`检测到${totalPages}页章节,书籍ID: ${bookId}`);

            // 然后通过POST请求获取其他页面的章节
            for(let page = 1; page <= totalPages; page++) {
                try {
                    console.log(`正在获取第${page}页章节...`);

                    // 构建POST请求参数
                    const postData = new URLSearchParams({
                        'id': bookId,
                        'page': page.toString()
                    });

                    // 发送POST请求获取章节列表
                    const response = await fetch('https://www.220book.com/index.php?action=loadChapterPage', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/x-www-form-urlencoded',
                            'X-Requested-With': 'XMLHttpRequest'
                        },
                        body: postData
                    });

                    if(response.ok) {
                        const jsonData = await response.json();
                        // 检查返回状态
                        if(jsonData.code === 0) {
                            // 从JSON数据中提取章节链接
                            jsonData.data.forEach(function(chapter) {
                                if(chapter.chapterurl) {
                                    g_chapterURLList.push(chapter.chapterurl);
                                }
                            });

                            // 添加延迟避免请求过快
                            await new Promise(resolve => setTimeout(resolve, 200));
                        } else {
                            console.warn(`第${page}页请求返回错误: ${jsonData.msg}`);
                        }
                    } else {
                        console.warn(`第${page}页请求失败: ${response.status}`);
                    }
                } catch(error) {
                    console.error(`获取第${page}页章节时出错:`, error);
                }
            }
            console.log(`章节列表获取完成,共${g_chapterURLList.length}章`);
        });

        //获取每一章的内容(单页模式)
        g_ctrlMap.set(rule_getChapterContentMode,fun_getChapterContenFromOnePage);
        g_ctrlMap.set(rule_getChapterTitle,function($data){
            return $data.find('h1.title').text().trim();
        });
        g_ctrlMap.set(rule_getChapterContentContainer,function($data){
            return $data.find('div#content');
        });
        g_ctrlMap.set(rule_getChapterLinesFromContainer,function($container){
            let lines = [];
            $container.find('p').each(function()
            {
                const $t = $(this);
                let txt = $t.text().trim();
                if(!txt.length) return;
                lines.push(txt);
            });
            return lines;
        });
    }
    // 笔趣阁-ryhy
    else if(fun_checkWebset(url,'https://www.ryhy.net/article/[0-9]*.html')){
        //local config
        {
            g_bTestDownload = false;    //是否测试下载
            g_bTestGetChapter = false;  //是否测试获取章节
            g_iTestDownloadCnt = 1;     //测试下载章节数
            g_iMaxPromiseCount = 3;     //批次处理总数
            g_batchSleep = 3;
        }

        //插入按钮
        g_ctrlMap.set(rule_appendDownloadBtn, function(newButton){
            $('.info .intro').prepend(newButton);
            return true;
        });

        //获取书名
        g_ctrlMap.set(rule_novelSaveName,[
            function(){return $('meta[property="og:novel:book_name"]').attr('content');},
            function(){return $('meta[property="og:novel:author"]').attr('content');}
        ]);

        //获取章节列表
        g_ctrlMap.set(rule_getChapterListMode,fun_getChapterListFromCurPage);
        g_ctrlMap.set(rule_getChapterListFromCurPage,function(){
            let dtCnt = 0;
            $('#list dd').each(function() {
                var url = $(this).find('a').attr('href');
                    if(url){
                        g_chapterURLList.push(url);
                    }
            });
        });

        //获取每一章的内容
        g_ctrlMap.set(rule_getChapterContentMode,fun_getChapterContenFromOnePage);
        g_ctrlMap.set(rule_getChapterTitle,function($data){return $data.find('.bookname h1:first').text();});
        g_ctrlMap.set(rule_getChapterContentContainer,function($data){return $data.find('#htmlContent');});
        g_ctrlMap.set(rule_getChapterLinesFromContainer,function($container){
            let lines = [];
            // 处理包含<p>标签和<br>换行符的HTML内容
            $container.find('p').each(function() {
                const $p = $(this);
                let paragraphText = $p.text().trim();
                if(paragraphText) {
                    // 将段落文本按<br>标签分割成多行
                    const htmlContent = $p.html();
                    const brSplitLines = htmlContent.split(/<br\s*\/?>/i);

                    if(brSplitLines.length > 1) {
                        // 如果有<br>标签,按换行分割
                        brSplitLines.forEach(lineHtml => {
                            // 移除HTML标签,只保留纯文本
                            const cleanText = lineHtml.replace(/<[^>]*>/g, '').trim();
                            if(cleanText) {
                                lines.push(cleanText);
                            }
                        });
                    } else {
                        // 如果没有<br>标签,直接添加整个段落
                        lines.push(paragraphText);
                    }
                }
            });

            // 如果没有找到<p>标签,回退到原来的文本节点处理方式
            if(lines.length === 0) {
                $container.contents().each(function() {
                    if (this.nodeType !== Node.TEXT_NODE) return;
                    let txt = this.textContent.trim();
                    if (txt !== "") {
                        lines.push(txt);
                    }
                });
            }

            return lines;
        });
    }
    // 笔趣阁-wtksm
    else if(fun_checkWebset(url,'https://www.wtksm.com/novel/*')){
        //local config
        {
            g_bTestDownload = false;    //是否测试下载
            g_bTestGetChapter = false;  //是否测试获取章节
            g_iTestDownloadCnt = 100;     //测试下载章节数
            g_iMaxPromiseCount = 5;     //批次处理总数
            g_batchSleep = 1;
        }

        //判断是否为书籍主页
        g_ctrlMap.set(rule_isBookMainPage,function(){
            return ($('dd table').length > 0);
        });

        //插入按钮
        g_ctrlMap.set(rule_appendDownloadBtn, function(newButton){
            $('dd table').prepend(newButton);
            return true;
        });

        //获取书名
        g_ctrlMap.set(rule_novelSaveName,[
            function(){return $('meta[property="og:novel:book_name"]').attr('content');},
            function(){return $('meta[property="og:novel:author"]').attr('content');}
        ]);

        //获取章节列表
        g_ctrlMap.set(rule_getChapterListMode,fun_getChapterListFromCurPage);
        g_ctrlMap.set(rule_getChapterListFromCurPage,function(){
            let dtCnt = 0;
            $('dd table a').each(function() {
                var url = $(this).attr('href');
                    if(url){
                        g_chapterURLList.push(url);
                    }
            });
        });

        //获取每一章的内容
        g_ctrlMap.set(rule_getChapterContentMode,fun_getChapterContenFromOnePage);
        g_ctrlMap.set(rule_getChapterTitle,function($data){return $data.find('.bdsub dd h1:first').text();});
        g_ctrlMap.set(rule_getChapterContentContainer,function($data){return $data.find('#htmlContent');});
        g_ctrlMap.set(rule_getChapterLinesFromContainer,function($container){
            let lines = [];
            // 处理包含<p>标签和<br>换行符的HTML内容
            $container.find('p').each(function() {
                const $p = $(this);
                let paragraphText = $p.text().trim();
                if(paragraphText) {
                    // 将段落文本按<br>标签分割成多行
                    const htmlContent = $p.html();
                    const brSplitLines = htmlContent.split(/<br\s*\/?>/i);

                    if(brSplitLines.length > 1) {
                        // 如果有<br>标签,按换行分割
                        brSplitLines.forEach(lineHtml => {
                            // 移除HTML标签,只保留纯文本
                            const cleanText = lineHtml.replace(/<[^>]*>/g, '').trim();
                            if(cleanText) {
                                lines.push(cleanText);
                            }
                        });
                    } else {
                        // 如果没有<br>标签,直接添加整个段落
                        lines.push(paragraphText);
                    }
                }
            });

            // 如果没有找到<p>标签,回退到原来的文本节点处理方式
            if(lines.length === 0) {
                $container.contents().each(function() {
                    if (this.nodeType !== Node.TEXT_NODE) return;
                    let txt = this.textContent.trim();
                    if (txt !== "") {
                        lines.push(txt);
                    }
                });
            }

            return lines;
        });
    }
    // 笔趣看-ynfdkj
    else if(fun_checkWebset(url,'https://www.ynfdkj.com/biquge/[0-9]*.html')){
        //local config
        {
            g_bTestDownload = true;    //是否测试下载
            g_bTestGetChapter = false;  //是否测试获取章节
            g_iTestDownloadCnt = 1;     //测试下载章节数
            g_iMaxPromiseCount = 5;     //批次处理总数
            g_batchSleep = 1;
        }

        //插入按钮
        g_ctrlMap.set(rule_appendDownloadBtn, function(newButton){
            $('.info .intro').prepend(newButton);
            return true;
        });

        //获取书名
        g_ctrlMap.set(rule_novelSaveName,[
            function(){return $('meta[property="og:novel:book_name"]').attr('content');},
            function(){return $('meta[property="og:novel:author"]').attr('content');}
        ]);

        //获取章节列表
        g_ctrlMap.set(rule_getChapterListMode,fun_getChapterListFromCurPage);
        g_ctrlMap.set(rule_getChapterListFromCurPage,function(){
            let dtCnt = 0;
            $('#list a').each(function() {
                var url = $(this).attr('href');
                    if(url){
                        g_chapterURLList.push(url);
                    }
            });
        });

        //获取每一章的内容
        g_ctrlMap.set(rule_getChapterContentMode,fun_getChapterContenFromOnePage);
        g_ctrlMap.set(rule_getChapterTitle,function($data){return $data.find('.readbar .bookname h1:first').text();});
        g_ctrlMap.set(rule_getChapterContentContainer,function($data){return $data.find('#htmlContent');});
        g_ctrlMap.set(rule_getChapterLinesFromContainer,function($container){
            let lines = [];
            // 处理包含<p>标签和<br>换行符的HTML内容
            $container.find('p').each(function() {
                const $p = $(this);
                let paragraphText = $p.text().trim();
                if(paragraphText) {
                    // 将段落文本按<br>标签分割成多行
                    const htmlContent = $p.html();
                    const brSplitLines = htmlContent.split(/<br\s*\/?>/i);

                    if(brSplitLines.length > 1) {
                        // 如果有<br>标签,按换行分割
                        brSplitLines.forEach(lineHtml => {
                            // 移除HTML标签,只保留纯文本
                            const cleanText = lineHtml.replace(/<[^>]*>/g, '').trim();
                            if(cleanText) {
                                lines.push(cleanText);
                            }
                        });
                    } else {
                        // 如果没有<br>标签,直接添加整个段落
                        lines.push(paragraphText);
                    }
                }
            });

            // 如果没有找到<p>标签,回退到原来的文本节点处理方式
            if(lines.length === 0) {
                $container.contents().each(function() {
                    if (this.nodeType !== Node.TEXT_NODE) return;
                    let txt = this.textContent.trim();
                    if (txt !== "") {
                        lines.push(txt);
                    }
                });
            }

            return lines;
        });
    }
    //[八二小说网](https://www.82xs.com)
    else if(fun_checkWebset(url,'https://www.82xs.com/bqg/[0-9]*.html') || fun_checkWebset(url,'https://www.82xs.com/index/[0-9]*/[0-9]*/') || fun_checkWebset(url,'https://m\.82xs\.com/[0-9]*/')){
        console.log("八二小说网配置已激活");

        //local config
        {
            g_bTestDownload = false;    //是否测试下载
            g_bTestGetChapter = false;  //是否测试获取章节
            g_iTestDownloadCnt = 3;     //测试下载章节数
            g_iMaxPromiseCount = 5;     //批次处理总数
            g_batchSleep = 2;
        }

       //判断是否为书籍主页
        g_ctrlMap.set(rule_isBookMainPage,function(){
            // 检查是否为手机端页面
            const isMobilePage = $('meta[name="applicable-device"][content="mobile"]').length > 0;

            if(isMobilePage) {
                // 手机端页面检测逻辑
                return $('meta[property="og:novel:book_name"]').length > 0 ||
                       $('div.book-info').length > 0 ||
                       $('div.chapter-list').length > 0;
            } else {
                // PC端页面检测逻辑
                return $('meta[property="og:novel:book_name"]').length > 0 ||
                       $('div.section-list').length > 0 ||
                       $('div.word_read').length > 0;
            }
        });

        //插入按钮
        g_ctrlMap.set(rule_appendDownloadBtn,function(newButton){
            console.log('在八二小说网页面插入下载按钮');

            // 检查是否为手机端页面
            const isMobilePage = $('meta[name="applicable-device"][content="mobile"]').length > 0;

            if(isMobilePage) {
                console.log('检测到手机端页面,使用手机端插入逻辑');
                // 手机端页面插入逻辑
                if($('div.book-info').length > 0) {
                    $('div.book-info').before(newButton);
                } else if($('div.chapter-list').length > 0) {
                    $('div.chapter-list').before(newButton);
                } else if($('ul.chapter-list').length > 0) {
                    $('ul.chapter-list').before(newButton);
                } else {
                    $('body').prepend(newButton);
                }
            } else {
                console.log('检测到PC端页面,使用PC端插入逻辑');
                // PC端页面插入逻辑
                if($('div.section-list').length > 0) {
                    $('div.section-list').before(newButton);
                } else if($('div.word_read').length > 0) {
                    $('div.word_read').before(newButton);
                } else if($('div[class*="chapter"]').length > 0) {
                    $('div[class*="chapter"]').first().before(newButton);
                } else {
                    $('body').prepend(newButton);
                }
            }

            return true;
        });

        // 2. 获取小说名和作者 (修复 TypeError 问题)
        g_ctrlMap.set(rule_novelSaveName, [
            function() {
                // 第一个函数:返回小说名称
                let title = $('meta[property="og:novel:book_name"]').attr('content');
                if(!title) {
                    // 兼容PC端和手机端的不同DOM结构
                    title = $('.bookname h1').text().trim() || $('.info .top h1').text().trim();
                }
                return title || '未知小说';
            },
            function() {
                // 第二个函数:返回作者名称
                let author = $('meta[property="og:novel:author"]').attr('content');
                if(!author) {
                    // 兼容PC端和手机端的不同DOM结构
                    author = $('.book-info .tag .author').text().replace(' 著', '').trim() || $('.info .fix p').first().text().replace('作\u00A0\u00A0者:', '').trim();
                }
                return author || '未知作者';
            }
        ]);

        //获取章节列表(链式翻页模式:当前页 -> 下一页 -> ...)
        g_ctrlMap.set(rule_getChapterListMode, fun_getChapterListCustom);
        g_ctrlMap.set(rule_getChapterListCustom, async function() {
            console.log("[DEBUG] --- 开始按页顺序获取章节列表 ---");

            // 用于记录已经处理过的URL,防止陷入死循环
            const processedUrls = new Set();
            let pageCount = 0;

            // 核心递归处理函数
            async function processPage(url, $pageDoc = null) {
                // 防死循环检测
                if (processedUrls.has(url)) {
                    console.log(`[DEBUG] 页面已处理过,跳过: ${url}`);
                    return;
                }
                processedUrls.add(url);
                pageCount++;
                console.log(`[DEBUG] ---> 正在处理第 ${pageCount} 页: ${url}`);

                let $doc = $pageDoc;

                // 如果没有传入文档对象(即非当前首页),则发起网络请求获取
                if (!$doc) {
                    try {
                        const response = await fetch(url);
                        if (!response.ok) {
                            console.error(`[DEBUG] 请求失败,状态码: ${response.status}`);
                            return;
                        }
                        const html = await response.text();
                        // 使用 DOMParser 解析完整 HTML,防止 jQuery 直接 $(html) 丢失部分标签
                        const parser = new DOMParser();
                        const doc = parser.parseFromString(html, "text/html");
                        $doc = $(doc);
                    } catch (e) {
                        console.error(`[DEBUG] 获取或解析页面失败: ${url}`, e);
                        return;
                    }
                }

                // --- 1. 提取当前页的章节列表 ---
                let newLinksCount = 0;

                // 兼容 PC 端和手机端的列表选择器
                let $lists = $doc.find('ul.section-list, div.section-list > ul, ul.chapter-list');

                $lists.each(function() {
                    let $ul = $(this);

                    // 【防乱序1】跳过手机端页面顶部的“最新章节”区块
                    let prevText = $ul.parent().prev('h3').text() || $ul.prev('h3').text();
                    if (prevText.includes('最新章节')) {
                        console.log('[DEBUG] 跳过手机端“最新章节”区块 (防止章节乱序)');
                        return; // 相当于 continue
                    }

                    let $lis = $ul.find('li');

                    // 【防乱序2】处理 PC 端顶部隐藏的最新章节 (通常带有 ycxsid 类)
                    if ($ul.hasClass('ycxsid') || $ul.parent().hasClass('ycxsid')) {
                        console.log('[DEBUG] 发现PC端隐藏的最新章节区块(ycxsid),跳过前9个');
                        $lis = $lis.slice(9);
                    }

                    // 遍历所有章节 <li>
                    $lis.each(function() {
                        let $a = $(this).find('a');
                        if ($a.length === 0) return;

                        let href = $a.attr('href');
                        // 确保是章节链接,且不是javascript
                        if (href && href.includes('/bqg/') && !href.includes('javascript')) {
                            if (!href.startsWith('http')) {
                                href = window.location.origin + href;
                            }
                            // 最终去重加入
                            if (!g_chapterURLList.includes(href)) {
                                g_chapterURLList.push(href);
                                newLinksCount++;
                            }
                        }
                    });
                });

                console.log(`[DEBUG] 本页有效获取 ${newLinksCount} 个新章节。当前总计: ${g_chapterURLList.length}`);

                // --- 2. 查找下一页链接 ---
                let nextPageUrl = null;
                // 寻找 class 为 y 的 a 标签,或者文本包含“下一页”的链接
                let $nextBtns = $doc.find('div.page_num a.y, a.index-container-btn, div.page a');

                $nextBtns.each(function() {
                    let text = $(this).text().trim();
                    let href = $(this).attr('href');
                    if (text.includes('下一页') && href && !href.includes('javascript')) {
                        if (!href.startsWith('http')) {
                            href = window.location.origin + href;
                        }
                        nextPageUrl = href;
                        return false; // 找到就退出 each 循环
                    }
                });

                // --- 3. 递归判断 ---
                // 如果找到了下一页,并且跟当前页URL不同,继续抓取
                if (nextPageUrl && nextPageUrl !== url) {
                    console.log(`[DEBUG] 发现下一页: ${nextPageUrl},准备获取...`);
                    await fun_sleep(500); // 稍微延迟防风控
                    await processPage(nextPageUrl);
                } else {
                    console.log(`[DEBUG] 没有发现下一页按钮,目录抓取完毕。`);
                }
            }

            // 【触发入口】直接使用当前页面的 URL 和已有的 document 对象作为第一页
            let currentUrl = window.location.href;
            await processPage(currentUrl, $(document));

            console.log(`[DEBUG] --- 章节列表提取结束,最终总章节数: ${g_chapterURLList.length} ---`);
        });


        // 4. 获取章节内容(单页模式:一页即一章)
        g_ctrlMap.set(rule_getChapterContentMode, fun_getChapterContenFromOnePage);

        // 提取章节标题(兼容 PC端的 h3 和手机端的 #chaptername)
        g_ctrlMap.set(rule_getChapterTitle, function($data) {
            let title = $data.find('.word_read h3, #chaptername').text() || $data.find('h1').text();
            return title.replace(/(第\d+页)/g, '').trim();
        });

        // 获取正文容器(兼容 PC端的 .word_read 和手机端的 #txt)
        g_ctrlMap.set(rule_getChapterContentContainer, function($data) {
            let $container = $data.find('.word_read, #txt');

            // 调试拦截:如果获取不到容器(长度为0)
            if ($container.length === 0) {
                let targetUrl = $data.filter('link[rel="canonical"]').attr('href')
                             || $data.find('link[rel="canonical"]').attr('href')
                             || '未知URL';
                console.error("[DEBUG] ❌ 获取章节正文容器失败!(未找到 .word_read 或 #txt)");
                console.error("[DEBUG] 失败章节所在的URL: ", targetUrl);
            }

            return $container;
        });

        // 核心解密逻辑:提取 Base64 编码的文本
        g_ctrlMap.set(rule_getChapterLinesFromContainer, function($container) {
            let lines = [];
            let htmlContent = $container.html() || '';

            // 解析正文中的 base64 加密字符串,例如: qsbs.bb('PHA+...')
            let reg = /qsbs\.bb\(['"](.*?)['"]\)/g;
            let match;
            while ((match = reg.exec(htmlContent)) !== null) {
                try {
                    let base64Str = match[1];
                    // 解码 Base64 -> utf-8 HTML片段
                    let decodedHtml = decodeURIComponent(escape(atob(base64Str)));
                    let text = $(decodedHtml).text().trim();
                    if (text) {
                        lines.push(text);
                    }
                } catch (e) {
                    console.error("正文 Base64 解码失败", e);
                }
            }

            // 补充抓取现有的可见 <p> 标签(排除可能渲染出来的重复项及站内广告)
            $container.find('p').each(function() {
                let text = $(this).text().trim();
                if (text && !text.includes('牢记最新域名') && !text.includes('请勿开启浏览器阅读模式') && !text.includes('相邻推荐') && !text.includes('花有重开日')) {
                     if (!lines.includes(text)) {
                         lines.push(text);
                     }
                }
            });

            return lines;
        });

        // 5. 最终文本净化过滤
        g_ctrlMap.set(rule_filterTxt, function(txt) {
            return txt.replace(/牢记最新域名.*?\\n/g, '')
                      .replace(/请勿开启浏览器阅读模式.*?\\n/g, '')
                      .replace(/相邻推荐.*?\\n/g, '')
                      .replace(/花有重开日.*?\\n/g, '');
        });
    }
    // [FC2 博客 - 猫と柿] 针对单页无列表小说的支持
    else if(fun_checkWebset(url, 'https://3tb4weatuybs\\.blog\\.fc2\\.com/blog-entry-.*\\.html'))
    {
        console.log("激活 FC2 博客单页小说下载规则");

        // local config
        {
            g_bTestDownload = false;    // 是否测试下载
            g_bTestGetChapter = false;  // 是否测试获取章节
            g_iMaxPromiseCount = 1;     // 单页只需要请求 1 次(即当前页本身)
            g_batchSleep = 0;           // 不需要批次睡眠
        }

        // 1. 插入下载按钮
        g_ctrlMap.set(rule_appendDownloadBtn, function(newButton){
            // 将下载按钮插入到文章标题下方的 entry-header-inner 区域
            let $headerInner = $('.entry-header-inner');
            if ($headerInner.length > 0) {
                $headerInner.append(newButton);
                // 简单加点样式让按钮在这个博客里好看一点(可选)
                newButton.css({
                    "margin-left": "15px",
                    "padding": "2px 10px",
                    "background": "#b89b7a",
                    "color": "#fff",
                    "border": "none",
                    "border-radius": "4px",
                    "cursor": "pointer"
                });
                return true;
            }
            return false;
        });

        // 2. 获取书名与作者
        g_ctrlMap.set(rule_novelSaveName, [
            function(){
                let fullTitle = $('meta[property="og:title"]').attr('content') || $('title').text();
                let bookNameMatch = fullTitle.match(/《(.*?)》/);
                let name = bookNameMatch ? bookNameMatch[1] : fullTitle;
                // 确保文件名也经由 OpenCC 转换为简体
                if (typeof OpenCC !== 'undefined' && OpenCC.Converter) {
                    return OpenCC.Converter({ from: 'tw', to: 'cn' })(name);
                }
                return name;
            },
            function(){
                let fullTitle = $('meta[property="og:title"]').attr('content') || $('title').text();
                let authorMatch = fullTitle.match(/by\s+(.+?)($||)/);
                let author = authorMatch ? authorMatch[1].trim() : "未知作者";
                if (typeof OpenCC !== 'undefined' && OpenCC.Converter) {
                    return OpenCC.Converter({ from: 'tw', to: 'cn' })(author);
                }
                return author;
            }
        ]);

        // 3. 核心修改:由于整本书就在这一页,章节列表就是“当前页自身”
        g_ctrlMap.set(rule_getChapterListMode, fun_getChapterListFromCurPage);
        g_ctrlMap.set(rule_getChapterListFromCurPage, function(){
            // 直接把当前页面的全链接塞入队列,作为唯一的“下载源”
            g_chapterURLList.push(window.location.href);
        });

        // 4. 获取这一页的全部小说正文
        g_ctrlMap.set(rule_getChapterContentMode, fun_getChapterContenFromOnePage);
        // 此站无须提取单独的“章节标题”,直接返回空(正文开头自带了第1章等字样)
        g_ctrlMap.set(rule_getChapterTitle, function($data){ return ''; }); 
        // 指定主体内容容器
        g_ctrlMap.set(rule_getChapterContentContainer, function($data){
            return $data.find('.inner-contents');
        });
        // 从容器中提取所有的段落
        g_ctrlMap.set(rule_getChapterLinesFromContainer, function($container){
            let lines = [];
            
            // 复制一个克隆体用来操作,防止删除干扰原网页
            let $clone = $container.clone();
            // 移除不需要的 FC2 拍手标签和一些可能存在的非正文组件
            $clone.find('script, a, iframe, div[id*="clap"], .entry-tag').remove();
            
            // 使用 html 并通过替换 <br> 换行符来切分段落,可以完美保留原排版
            let htmlContent = $clone.html() || '';
            // 将所有 <br> 或 <br/> 替换成统一的换行标识符,然后按换行符切割
            let RawLines = htmlContent.replace(/<br\s*\/?>/gi, '\n').split('\n');

            let totalLines = RawLines.length;
            let currentLine = 0;
            let lastPercent = -1; // 用于记录上一次打印的百分比整数

            console.log(`开始清洗正文文本,总原始行数: ${totalLines}`);
            
            RawLines.forEach(function(line) {

                currentLine++;

                // 计算当前进度的百分比整数 (0 到 100)
                let currentPercent = Math.floor((currentLine / totalLines) * 100);

                // 只有当百分比整数增长时,才打印进度
                if (currentPercent > lastPercent) {
                    console.log(`正文文本清洗进度: ${currentPercent}% (${currentLine}/${totalLines} 行)`);
                    lastPercent = currentPercent; // 更新记录点
                }

                // 利用 jQuery 的 text() 剥离残留的 HTML 标签(如 <font>, <strong>, <span> 等)
                let cleanText = $('<div>').html(line).text().trim();
                
                // 过滤掉沉余信息:如翻译插件加载提示、空白行
                if (cleanText && 
                    !cleanText.includes('送出拍手') && 
                    !cleanText.includes('缺的章節已補') && 
                    !cleanText.includes('番外更新') &&
                    !cleanText.includes('文案:')) {

                   // 【核心改动】如果繁转简库可用,直接将本行文本转为简体
                   if (typeof OpenCC !== 'undefined' && OpenCC.Converter) {
                        cleanText = OpenCC.Converter({ from: 'tw', to: 'cn' })(cleanText);
                    }
                    lines.push(cleanText);
                }
            });
            return lines;
        });
    }
    else{
        //插入按钮
        //获取书名
        //获取章节列表
        //获取每一章的内容
        console.log("不是书籍主页,脚本不生效!");
    }

    console.log('size:'+g_ctrlMap.size);



    //最终配置
    if(g_ctrlMap.size)
    {
        window.onload=function()
        {
            fun_downloadConfig();
        }
    }

})();