天天看小说

https://cn.ttkan.co

autobcb_admin (12020)3天前

天天看小说 作者 星辰

二维码导入(APP尚未完成该功能)
{
    "bookSourceUrl": "https:\/\/cn.ttkan.co",
    "bookSourceName": "天天看小说",
    "enabledExplore": true,
    "enabled": true,
    "bookSourceGroup": "雨落星辰",
    "author": "星辰",
    "help": true,
    "html": "\n<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"UTF-8\">\n    <title>天天看小说<\/title>\n<body>\n\n<\/body>\n<script src=\"https:\/\/vc.jd.com\/web\/js\/jquery-3.1.1.min.js\"><\/script>\n<!--如果要引入外部 js 必须在书源代码的上面-->\n<script>\n    var isCookieJar = true;\/\/ 不需要CookieJar请修改此处\n    class FlutterJSBridge {\n        constructor() {\n            this.init(); \/\/前台webview 里必须删除这行\n        }\n\n        init() {\n            if (window.flutter_inappwebview) {\n                this.isReady = true;\n                this.CookieJar();\n            } else {\n                window.addEventListener('flutterInAppWebViewPlatformReady', () => {\n                    this.isReady = true;\n                    console.log('JSBridge初始化完成');\n                    this.CookieJar();\n                });\n            }\n        }\n\n        \/\/通知原生页面初始化完成,仅在书源和tts生效,webview请勿使用,只有通知加载成功后才允许运行,否则会一直等待加载成功\n        async CookieJar() {\n            try {\n                await window.flutter_inappwebview.callHandler('CookieJar', isCookieJar);\n            } catch (error) {\n                console.error('汇报完成准备失败:', error);\n            }\n        }\n\n        \/\/获取应用编译版本\n        async getbuildNumber() {\n            try {\n                return await window.flutter_inappwebview.callHandler('buildNumber');\n            } catch (error) {\n                return 0;\n            }\n        }\n\n        \/\/获取应用版本\n        async getversion() {\n            try {\n                return await window.flutter_inappwebview.callHandler('version');\n            } catch (error) {\n                return \"0.0.0\";\n            }\n        }\n\n        \/\/获取设备唯一id\n        async getDeviceid() {\n            try {\n                return await window.flutter_inappwebview.callHandler('id');\n            } catch (error) {\n                return \"\";\n            }\n        }\n\n        \/\/获取设备平台 此处返回 windows、macos、ios、ohos、android\n        async getDevice() {\n            try {\n                return await window.flutter_inappwebview.callHandler('device');\n            } catch (error) {\n                return \"\";\n            }\n        }\n\n        \/\/输出日志,前台webview请勿使用\n        \/\/str 为 String\n        async log(str) {\n            try {\n                return await window.flutter_inappwebview.callHandler('log', str);\n            } catch (error) {\n                return false;\n            }\n        }\n\n        \/\/书源调试时可输出 html 代码到前台\n        \/\/type 0 搜索源码 , 1详情源码 ,2目录源码 ,3正文源码\n        \/\/str 为 String\n        \/\/type 为int\n        async text(type, str) {\n            try {\n                return await window.flutter_inappwebview.callHandler('text', type, str);\n            } catch (error) {\n                return false;\n            }\n        }\n\n        \/\/toast弹窗\n        \/\/str 为 String\n        async showToast(str) {\n            try {\n                return await window.flutter_inappwebview.callHandler('showToast', str);\n            } catch (error) {\n                return false;\n            }\n        }\n\n        \/\/获取默认ua\n        async getWebViewUA() {\n            try {\n                return await window.flutter_inappwebview.callHandler('getWebViewUA');\n            } catch (error) {\n                return \"\";\n            }\n        }\n\n        \/\/通过url打开外部应用\n        \/\/url 为 String\n        async openurl(url) {\n            try {\n                return await window.flutter_inappwebview.callHandler('openurl', url, \"\");\n            } catch (error) {\n                return false;\n            }\n        }\n\n        \/\/通过url打开外部应用并附带mimeType\n        \/\/url 为 String\n        \/\/mimeType 为 String\n        async openurlwithMimeType(url, mimeType) {\n            try {\n                return await window.flutter_inappwebview.callHandler('openurl', url, mimeType);\n            } catch (error) {\n                return false;\n            }\n        }\n\n        \/**\n         * 使用webView访问网络\n         * @param html 直接用webView载入的html, 如果html为空直接访问url\n         * @param url html内如果有相对路径的资源不传入url访问不了\n         * @param js 用来取返回值的js语句, 没有就返回整个源代码\n         * @param body 当参数不为空的时候,会以post请求,此时请务必在 header 中带上content-type\n         * @param header 请求的header头,此参数必须是json字符串\n         * @return 返回js获取的内容\n         *\/\n        async webview(url, js, html, body, header) {\n            try {\n                return await window.flutter_inappwebview.callHandler('webview', url, js, html, body, header, \"\", \"\");\n            } catch (error) {\n                return \"\";\n            }\n        }\n\n        \/**\n         * overrideUrlRegex 为正则表达式\n         * 使用方法和上面的一样\n         * 但返回的内容为正则到的内容,如果无法正则到则返回 js 获取的内容,如果 js 为空则返回页面 html\n         *\/\n        async webViewGetOverrideUrl(url, js, html, body, header, overrideUrlRegex) {\n            try {\n                return await window.flutter_inappwebview.callHandler('webview', url, js, html, body, header, overrideUrlRegex, \"\");\n            } catch (error) {\n                return \"\";\n            }\n        }\n\n        \/**\n         * 使用webView获取资源url\n         * urlregex 为正则表达式\n         * 使用方法和上面的一样\n         * 但返回的内容为正则到的内容,如果无法正则到则返回 js 获取的内容,如果 js 为空则返回页面 html\n         *\/\n        async webViewGetSource(url, js, html, body, header, urlregex) {\n            try {\n                return await window.flutter_inappwebview.callHandler('webview', url, js, html, body, header, \"\", urlregex);\n            } catch (error) {\n                return \"\";\n            }\n        }\n\n\n        \/**\n         * 启动前台 webview 访问链接并获取结束时的 html,可用于手工过盾\n         * @param url 网址\n         * @param title 标题\n         * @param header 请求的header头,此参数必须是json字符串\n         * @return 返回网页的内容\n         *\/\n        async startBrowser(url, title, header) {\n            try {\n                return await window.flutter_inappwebview.callHandler('startBrowser', url, title, header);\n            } catch (error) {\n                return \"\";\n            }\n        }\n\n        \/\/专门为段评设置的半屏显示,不返回任何东西\n        async startBrowserDp(url, title) {\n            try {\n                return await window.flutter_inappwebview.callHandler('startBrowserDp', url, title);\n            } catch (error) {\n                return \"\";\n            }\n        }\n\n        \/\/仅前台webview可以使用,返回按钮,返回上一个页面\n        async back() {\n            try {\n                return await window.flutter_inappwebview.callHandler('back');\n            } catch (error) {\n                return false;\n            }\n        }\n\n        \/\/将 utf8字符串转到 gbk 并 url 编码\n        async utf8ToGbkUrlEncoded(str) {\n            try {\n                return await window.flutter_inappwebview.callHandler('utf8ToGbkUrlEncoded', str);\n            } catch (error) {\n                return \"\";\n            }\n        }\n\n        \/*\n        * @param str为图片链接\n        * @param header 请求的header头,此参数必须是json字符串\n        * 此函数是让用户输入图片中的验证码,当链接为空则直接让用户输入验证码\n        *\/\n        async getVerificationCode(str, header) {\n            try {\n                return await window.flutter_inappwebview.callHandler('getVerificationCode', str, header);\n            } catch (error) {\n                return \"\";\n            }\n        }\n\n        \/\/提交内容书本信息 json 后的字符串\n        async addbook(book) {\n            try {\n                return await window.flutter_inappwebview.callHandler('addbook', book);\n            } catch (error) {\n                return \"\";\n            }\n        }\n\n    }\n\n    \/\/webview请勿使用\n    \/\/以下提交的url,headers,body 都必须为字符串,headers必须为json字符串\n    \/\/当followRedirects 为 false 时不处理重定向,当为 true 时会自动处理重定向 ,如不明白用途直接用 true 最佳\n    \/\/ 以下所有参数除当followRedirects外均为 String\n    class Http {\n        constructor() { }\n\n        \/*\n         * 通用返回字段\n         * method post get 或者 head\n         * body 请求返回后的字节的 base64\n         * headers  map<String,List<String>> 可通过headers[\"\"]来或者\n         * statusCode 状态码\n         * statusMessage\n         * data 返回后的字节 格式化后的内容\n         *\/\n        async Get(url, headers, followRedirects) {\n            try {\n                return await window.flutter_inappwebview.callHandler('http', \"get\", url, \"\", JSON.stringify(headers), followRedirects, \"\");\n            } catch (error) {\n                return null;\n            }\n        }\n\n        async Head(url, headers, followRedirects) {\n            try {\n                return await window.flutter_inappwebview.callHandler('http', \"head\", url, \"\", JSON.stringify(headers), followRedirects, \"\");\n            } catch (error) {\n                return null;\n            }\n        }\n\n\n        async Post(url, headers, body, contenttype, followRedirects) {\n            try {\n                return await window.flutter_inappwebview.callHandler('http', \"post\", url, body, JSON.stringify(headers), followRedirects, contenttype);\n            } catch (error) {\n                return null;\n            }\n        }\n    }\n\n    class Cache {\n        constructor() { }\n        async get(key) {\n            try {\n                return await window.flutter_inappwebview.callHandler('cache.get', key);\n            } catch (error) {\n                return null;\n            }\n        }\n\n        async set(key, value) {\n            try {\n                return await window.flutter_inappwebview.callHandler('cache.set', key, value);\n            } catch (error) {\n                return null;\n            }\n        }\n\n        async remove(key) {\n            try {\n                return await window.flutter_inappwebview.callHandler('cache.remove', key);\n            } catch (error) {\n                return null;\n            }\n        }\n\n        \/\/如果登录为弹窗格式的,里面输入框输入的内容可以通过这个函数获取,默认返回的json格式或者为空,需要自行转换\n        async getLoginInfo() {\n            return await this.get(\"LoginInfo\")\n        }\n\n        \/\/将修改后的弹窗输入内容报错 ,必须 JSON.stringify,不然会出错\n        async putLoginInfo(info) {\n            return await this.set(\"LoginInfo\", info)\n        }\n\n        \/\/获取书本变量\n        async getbookVariable(bookurl) {\n            return await this.get(bookurl)\n        }\n\n        \/\/写入书本变量\n        async setbookVariable(bookurl, value) {\n            return await this.set(bookurl, value)\n        }\n    }\n\n    class Cookie {\n        constructor() { }\n\n        \/\/通过url获取当前url的所有cookie\n        async get(url) {\n            try {\n                return await window.flutter_inappwebview.callHandler('cookie.get', url);\n            } catch (error) {\n                return null;\n            }\n        }\n\n        \/\/通过url删除当前url的所有cookie\n        async remove(url) {\n            try {\n                return await window.flutter_inappwebview.callHandler('cookie.remove', url);\n            } catch (error) {\n                return null;\n            }\n        }\n\n\n        \/\/通过url保存当前url的所有cookie\n        async set(url, value) {\n            try {\n                return await window.flutter_inappwebview.callHandler('cookie.set', url, value);\n            } catch (error) {\n                return null;\n            }\n        }\n\n        \/\/通过 url 获取单个 cookie 的值\n        async getCookie(url, value) {\n            try {\n                return await window.flutter_inappwebview.callHandler('cookie.getCookie', url, value);\n            } catch (error) {\n                return null;\n            }\n        }\n    }\n\n    \/\/安全的创建一个 div 解析 html\n    function parseHTMLSafely(htmlStr) {\n        try {\n            \/\/ 在函数作用域内创建独立的临时容器\n            \/\/ 每个调用创建新的jQuery对象,互不影响\n            var tempDiv = document.createElement('div');\n            tempDiv.innerHTML = htmlStr;\n            return $(tempDiv);\n        } catch (e) {\n            flutterBridge.log(\"HTML解析错误:\" + e.message);\n            return $('<div>');\n        }\n    }\n\n    \/\/parseHTMLSafely 创建的用完后必须删除\n    function removeHTMLSafely(tempContainer) {\n        try {\n            tempContainer.innerHTML = '';\n            if (tempContainer.parentNode) {\n                tempContainer.parentNode.removeChild(tempContainer);\n            }\n        } catch (e) {\n            flutterBridge.log(\"HTML移除失败:\" + e.message);\n        }\n    }\n\n    \/\/移除 css js,创建parseHTMLSafely前如果用不上 cssjs 建议移除\n    function removeHTMLTags(htmlString) {\n        \/\/ 移除script标签\n        let result = htmlString.replace(\/<script\\b[^<]*(?:(?!<\\\/script>)<[^<]*)*<\\\/script>\/gi, '');\n        \/\/ 移除style标签\n        result = result.replace(\/<style\\b[^<]*(?:(?!<\\\/style>)<[^<]*)*<\\\/style>\/gi, '');\n        return result;\n    }\n\n<\/script>\n<script>\n    const flutterBridge = new FlutterJSBridge();\n    const cache = new Cache();\n    const http = new Http();\n    const cookie = new Cookie();\n    var baseurl = \"https:\/\/cn.ttkan.co\"\n    var header = { \"User-Agent\": \"Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/131.0.0.0 Safari\/537.36\" };\n\n    \/\/ 搜索函数暂不可用\n    async function search(key, page) {\n        if (page > 1) {\n            return \"[]\";\n        }\n\n        try {\n            var url = baseurl + \"\/i\/sor.aspx?key=\" + encodeURIComponent(key);\n            var myheader = {\n                ...header,\n                \"Referer\": baseurl\n            };\n\n            var get = await http.Get(url, JSON.stringify(myheader), true);\n            flutterBridge.text(0, get.data);\n\n            return parseBookList(get.data);\n        } catch (e) {\n            flutterBridge.log(\"搜索出错:\" + e.message);\n            return \"[]\";\n        }\n    }\n\n    \/\/ 解析书籍列表\n    function parseBookList(html) {\n        try {\n            var $tempContainer = parseHTMLSafely(removeHTMLTags(html));\n            var books = [];\n            var bookUrls = new Set(); \/\/ 用于去重\n\n            \/\/ 查找所有h3标题下的书籍链接\n            $tempContainer.find(\"h3 a\").each(function () {\n                var $link = $(this);\n                var href = $link.attr('href');\n\n                \/\/ 必须有href属性\n                if (!href) {\n                    return true; \/\/ continue\n                }\n\n                \/\/ 过滤掉非书籍链接\n                if (href.includes('\/i\/') || href.includes('\/fenlei\/') ||\n                    href.includes('\/paihang\/') || href.includes('\/wanjie\/') ||\n                    href.includes('\/zuixin\/') || href.includes('.html') ||\n                    href.includes('javascript:') || href.includes('\/zuozhe\/')) {\n                    return true; \/\/ continue\n                }\n\n                \/\/ 提取书籍ID,格式如 \/167\/ 或 https:\/\/www.sudugu.org\/167\/\n                var match = href.match(\/\\\/(\\d+)\\\/?$\/);\n                if (!match) {\n                    return true; \/\/ continue\n                }\n\n                var bookId = match[1];\n                var bookUrl = \"\";\n                if (href.startsWith('http')) {\n                    bookUrl = href;\n                } else if (href.startsWith('\/')) {\n                    bookUrl = baseurl + href;\n                } else {\n                    return true; \/\/ continue\n                }\n\n                \/\/ 确保URL以\/结尾\n                if (!bookUrl.endsWith('\/')) {\n                    bookUrl += '\/';\n                }\n\n                \/\/ 去重\n                if (bookUrls.has(bookUrl)) {\n                    return true; \/\/ continue\n                }\n                bookUrls.add(bookUrl);\n\n                \/\/ 提取书名\n                var name = $link.text().trim();\n                if (!name || name.length < 2) {\n                    return true; \/\/ continue\n                }\n\n                \/\/ 查找父容器(.item或.itemtxt)获取更多信息\n                var $container = $link.closest('.item, div');\n\n                \/\/ 提取作者 - 查找包含\"作者:\"的链接\n                var author = \"\";\n                var $authorLink = $container.find(\"a[href*='\/zuozhe\/']\").first();\n                if ($authorLink.length > 0) {\n                    var authorText = $authorLink.text().trim();\n                    \/\/ 移除\"作者:\"前缀\n                    author = authorText.replace(\/^作者[:\\s]*\/, '');\n                }\n\n                \/\/ 提取分类和状态 - 从span标签中提取\n                var kind = \"\";\n                var status = \"\";\n                $container.find(\"span\").each(function () {\n                    var text = $(this).text().trim();\n                    if (text === '已完结' || text === '连载中') {\n                        status = text;\n                    } else if (text && text.length > 0 && text.length < 10 && !text.includes('更新')) {\n                        \/\/ 可能是分类\n                        if (!kind) {\n                            kind = text;\n                        }\n                    }\n                });\n\n\n                \/\/ 提取封面 - 在同一容器中查找img(优先查找.item容器)\n                var coverUrl = \"\";\n                var $itemContainer = $link.closest('.item');\n                var $img = $itemContainer.length > 0 ? $itemContainer.find(\"img\").first() : $container.find(\"img\").first();\n                if ($img.length > 0) {\n                    var src = $img.attr('src');\n                    if (src) {\n                        if (src.startsWith('http')) {\n                            coverUrl = src;\n                        } else if (src.startsWith('\/')) {\n                            coverUrl = baseurl + src;\n                        }\n                    }\n                }\n\n\n                \/\/ 提取最新章节 - 查找章节链接(包含.html的链接)\n                var latestChapterTitle = \"\";\n                var $chapterLinks = $container.find(\"a[href*='.html']\");\n                if ($chapterLinks.length > 0) {\n                    \/\/ 取第一个章节链接(最新章节)\n                    latestChapterTitle = $chapterLinks.first().text().trim();\n                }\n\n                var book = {\n                    \"bookUrl\": bookUrl,\n                    \"name\": name,\n                    \"author\": author,\n                    \"kind\": kind,\n                    \"coverUrl\": coverUrl,\n                    \"intro\": \"\",\n                    \"tocUrl\": bookUrl,\n                    \"wordCount\": status,  \/\/ 将状态信息放在wordCount字段\n                    \"type\": 0,\n                    \"latestChapterTitle\": latestChapterTitle\n                };\n\n                books.push(book);\n            });\n\n            removeHTMLSafely($tempContainer);\n            return JSON.stringify(books);\n        } catch (e) {\n            flutterBridge.log(\"解析书籍列表出错:\" + e.message);\n            return \"[]\";\n        }\n    }\n    \/\/ 书籍详情函数\n    \/\/ 书籍详情函数\nasync function info(bookurl) {\n    try {\n        var get = await http.Get(bookurl, JSON.stringify(header), true);\n        flutterBridge.text(1, get.data);\n\n        var $tempContainer = parseHTMLSafely(removeHTMLTags(get.data));\n        \/\/ 核心:定位详情核心容器、封面专属容器(均用多class精准匹配,无全局干扰)\n        var $infoWrap = $tempContainer.find(\".pure-u-xl-5-6.pure-u-lg-5-6.pure-u-md-2-3.pure-u-1-2\").first();\n        var $coverWrap = $tempContainer.find(\".pure-u-xl-1-6.pure-u-lg-1-6.pure-u-md-1-3.pure-u-1-2\").first();\n        \/\/ 初始化所有返回字段\n        var name = \"\", author = \"\", coverUrl = \"\", kind = \"\", intro = \"\", wordCount = \"\", latestChapterTitle = \"\";\n\n        \/\/ 1. 提取书名:容器内ul>li>h1 直接取文本(适配给定结构)\n        if ($infoWrap.length > 0) {\n            name = $infoWrap.find(\"ul li h1\").first().text().trim() || \"\";\n        }\n        \/\/ 备用方案:全局h1(防止容器class变化)\n        if (!name) {\n            name = $tempContainer.find(\"h1\").first().text().trim() || \"\";\n        }\n\n        \/\/ 2. 提取作者:匹配【作者:】span,取其后续兄弟a标签的文本\n        if ($infoWrap.length > 0) {\n            var $authorSpan = $infoWrap.find(\"ul li span:contains('作者:')\").first();\n            if ($authorSpan.length > 0) {\n                author = $authorSpan.next(\"a\").text().trim() || \"\";\n            }\n        }\n        \/\/ 备用方案:全局匹配\n        if (!author) {\n            author = $tempContainer.find(\"span:contains('作者:')\").next(\"a\").text().trim() || \"\";\n        }\n\n        \/\/ 3. 提取分类:匹配【类别:】span,取其父li的文本并剔除前缀\n        if ($infoWrap.length > 0) {\n            var $kindSpan = $infoWrap.find(\"ul li span:contains('类别:')\").first();\n            if ($kindSpan.length > 0) {\n                kind = $kindSpan.parent(\"li\").text().trim().replace(\/^类别:\/, \"\").trim() || \"\";\n            }\n        }\n        \/\/ 备用方案:全局匹配\n        if (!kind) {\n            kind = $tempContainer.find(\"span:contains('类别:')\").parent(\"li\").text().trim().replace(\/^类别:\/, \"\").trim() || \"\";\n        }\n\n        \/\/ 4. 提取封面:【精准匹配封面专属容器】(核心修改)\n        \/\/ 优先从封面容器内取amp-img的src(结构里已是完整http地址,直接用)\n        if ($coverWrap.length > 0) {\n            var $coverAmpImg = $coverWrap.find(\"amp-img\").first();\n            if ($coverAmpImg.length > 0) {\n                coverUrl = $coverAmpImg.attr(\"src\") || \"\";\n            }\n        }\n        \/\/ 备用兜底逻辑:容器匹配失败时,全局找amp-img→普通img,兼容各种情况\n        if (!coverUrl) {\n            var $ampImg = $tempContainer.find(\"amp-img\").first();\n            if ($ampImg.length > 0) {\n                coverUrl = $ampImg.attr(\"src\") || $ampImg.attr(\"data-src\") || \"\";\n            } else {\n                var $normalImg = $tempContainer.find(\"img\").first();\n                if ($normalImg.length > 0) {\n                    coverUrl = $normalImg.attr(\"src\") || $normalImg.attr(\"data-src\") || \"\";\n                }\n            }\n            \/\/ 兜底拼接baseurl(仅备用逻辑需要,精准匹配无需)\n            if (coverUrl && !coverUrl.startsWith('http') && coverUrl.startsWith('\/')) {\n                coverUrl = baseurl + coverUrl;\n            }\n        }\n        \/\/ 最终去重空格,避免空值\/无效值\n        coverUrl = coverUrl.trim();\n\n        \/\/ 5. 提取简介:优先精准匹配.description容器,无结果则走原兜底逻辑\n        var $descContainer = $tempContainer.find(\".description\").first();\n        if ($descContainer.length > 0) {\n            intro = $descContainer.text().trim().replace(\/\\s+\/g, \" \");\n        } else {\n            var $introContainer = $tempContainer.clone();\n            $introContainer.find(\"#list, #pages, .pages, .bookmark, .pure-u-xl-5-6\").remove();\n            $introContainer.find(\"p, div, section\").each(function () {\n                var text = $(this).text().trim();\n                if (text.length > 50 && text.length < 1000 &&\n                    !text.includes(\"最新章节\") && !text.includes(\"作者\") && !text.includes(\"更新时间\") &&\n                    !text.includes(\"第\") && !text.includes(\"章\") && !text.includes(\"收藏本书\")) {\n                    intro = text;\n                    return false;\n                }\n            });\n            removeHTMLSafely($introContainer);\n        }\n\n        \/\/ 6. 提取字数:保留原通用逻辑(结构未展示,备用)\n        $tempContainer.find(\"*\").each(function () {\n            var text = $(this).clone().children().remove().end().text().trim();\n            if (text.match(\/^[\\d.]+万字$\/)) {\n                wordCount = text;\n                return false;\n            }\n        });\n\n        \/\/ 7. 提取最新章节:排除收藏的a标签,精准匹配章节链接\n        var $chapterLinks = $tempContainer.find(\"a[href*='.html']\").not(\".anchor_bookmark\");\n        if ($chapterLinks.length > 0) {\n            latestChapterTitle = $chapterLinks.first().text().trim() || \"\";\n        }\n\n        \/\/ 构造返回对象(保留原所有字段,兼容原有调用)\n        var book = {\n            \"bookUrl\": bookurl,\n            \"name\": name,\n            \"author\": author,\n            \"kind\": kind,\n            \"coverUrl\": coverUrl,\n            \"intro\": intro,\n            \"tocUrl\": bookurl,\n            \"wordCount\": wordCount,\n            \"type\": 0,\n            \"latestChapterTitle\": latestChapterTitle\n        };\n\n        \/\/ 销毁临时容器,避免内存占用\n        removeHTMLSafely($tempContainer);\n        \/\/ 返回JSON字符串,与原函数一致\n        return JSON.stringify(book);\n    } catch (e) {\n        flutterBridge.log(\"获取详情出错:\" + e.message);\n        return \"{}\";\n    }\n}\n    \/\/ 章节列表函数(支持分页)\n    async function chapter(tocUrl) {\n        try {\n            var allChapters = [];\n            var pageIndex = 0;\n\n            while (true) {\n                var pageUrl;\n                if (pageIndex === 0) {\n                    pageUrl = tocUrl;\n                } else {\n                    var baseUrl = tocUrl.endsWith('\/') ? tocUrl.slice(0, -1) : tocUrl;\n                    pageUrl = baseUrl + \"\/p-\" + (pageIndex + 1) + \".html\";\n                }\n\n                flutterBridge.log(\"正在获取第\" + (pageIndex + 1) + \"页: \" + pageUrl);\n\n                var result = await getChapterPage(pageUrl);\n\n                if (result.chapters.length > 0) {\n                    allChapters.push(...result.chapters);\n                    flutterBridge.log(\"第\" + (pageIndex + 1) + \"页获取到 \" + result.chapters.length + \" 章\");\n                } else {\n                    flutterBridge.log(\"第\" + (pageIndex + 1) + \"页没有章节,停止获取\");\n                    break;\n                }\n\n                if (!result.hasNextPage) {\n                    flutterBridge.log(\"没有下一页,总共获取 \" + allChapters.length + \" 章\");\n                    break;\n                }\n\n                pageIndex++;\n                if (pageIndex >= 100) {\n                    flutterBridge.log(\"目录页数超过100页,停止获取\");\n                    break;\n                }\n            }\n\n            \/\/ 重新设置index\n            for (var i = 0; i < allChapters.length; i++) {\n                allChapters[i].index = i;\n            }\n\n            return JSON.stringify(allChapters);\n        } catch (e) {\n            flutterBridge.log(\"获取章节列表出错:\" + e.message);\n            return \"[]\";\n        }\n    }\n\n    \/\/ 获取单页章节\nasync function getChapterPage(pageUrl) {\n    try {\n        var get = await http.Get(pageUrl, JSON.stringify(header), true);\n        flutterBridge.text(2, get.data);\n\n        var $tempContainer = parseHTMLSafely(removeHTMLTags(get.data));\n        var chapters = [];\n        var hasNextPage = false;\n\n        var novelId = \"\";\n        var match = pageUrl.match(\/\\\/(\\d+)\\\/?\/);\n        if (match) {\n            novelId = match[1];\n        }\n\n        var $listContainer = $tempContainer.find(\"#list\");\n        var $chapterLinks = $listContainer.length > 0 ? $listContainer.find(\"a[href*='.html']\") : $tempContainer.find(\"a[href*='.html']\");\n\n        var index = 0;\n        $chapterLinks.each(function () {\n            var $link = $(this);\n            var chapterName = $link.text().trim();\n            var href = $link.attr('href');\n\n            if (!chapterName || !href || chapterName.length < 2) {\n                return true;\n            }\n\n            \/\/ 核心修复:添加「收藏本书」的过滤,同时保留原有所有排除条件\n            if (chapterName.includes(\"下载\") || chapterName.includes(\"TXT\") || chapterName.includes(\"目录\") ||\n                chapterName.includes(\"下一页\") || chapterName.includes(\"上一页\") || chapterName.includes(\"开始阅读\") ||\n                chapterName.includes(\"最新章节\") || chapterName === \"全部\" || chapterName.includes(\"收藏本书\")) {\n                return true;\n            }\n\n            var chapterId = \"\";\n            if (href.startsWith('http')) {\n                chapterId = href;\n            } else if (href.startsWith('\/')) {\n                chapterId = baseurl + href;\n            } else {\n                chapterId = baseurl + \"\/\" + novelId + \"\/\" + href;\n            }\n\n            chapters.push({\n                \"name\": chapterName,\n                \"chapterId\": chapterId,\n                \"index\": index,\n                \"isPay\": false,\n                \"isVip\": false,\n                \"isVolume\": false,\n                \"tag\": \"\"\n            });\n            index++;\n        });\n\n        $tempContainer.find(\"a\").each(function () {\n            var text = $(this).text().trim();\n            if (text === \"下一页\" || text.includes(\"下一页\")) {\n                hasNextPage = true;\n                return false;\n            }\n        });\n\n        removeHTMLSafely($tempContainer);\n        return { chapters: chapters, hasNextPage: hasNextPage };\n    } catch (e) {\n        flutterBridge.log(\"获取章节页出错:\" + e.message);\n        return { chapters: [], hasNextPage: false };\n    }\n}\n\n    \/\/ 章节内容函数(支持分页章节)\n    async function content(url) {\n        try {\n            var allContent = [];\n            var currentUrl = url;\n            var pageIndex = 1;\n            var maxPages = 10; \/\/ 防止无限循环\n\n            while (pageIndex <= maxPages) {\n                var get = await http.Get(currentUrl, JSON.stringify(header), true);\n                if (pageIndex === 1) {\n                    flutterBridge.text(3, get.data);\n                }\n\n                var $tempContainer = parseHTMLSafely(removeHTMLTags(get.data));\n\n                var $content = $tempContainer.find(\".content\").first();\n\n                if ($content.length > 0) {\n                    \/\/ 移除广告和无关元素\n                    $content.find(\"script, style, .ad, .adsbygoogle, .advertisement\").remove();\n\n                    \/\/ 提取段落内容\n                    var paragraphs = [];\n                    $content.find(\"p\").each(function () {\n                        var text = $(this).text().trim();\n                        if (text.length > 0) {\n                            paragraphs.push(text);\n                        }\n                    });\n\n                    \/\/ 如果没有p标签,直接获取文本\n                    if (paragraphs.length === 0) {\n                        var text = $content.text().trim();\n                        if (text.length > 0) {\n                            paragraphs.push(text);\n                        }\n                    }\n\n                    if (paragraphs.length > 0) {\n                        allContent.push(paragraphs.join('\\r\\n\\r\\n'));\n                    }\n                } else {\n                    break;\n                }\n\n                \/\/ 检查是否有下一页\n                var hasNextPage = false;\n                $tempContainer.find(\"a\").each(function () {\n                    var text = $(this).text().trim();\n                    var href = $(this).attr('href');\n                    if ((text === \"下一页\" || text.includes(\"下一页\")) && href) {\n                        \/\/ 构建下一页URL\n                        if (href.startsWith('http')) {\n                            currentUrl = href;\n                        } else if (href.startsWith('\/')) {\n                            currentUrl = baseurl + href;\n                        } else {\n                            \/\/ 相对路径,需要基于当前URL构建\n                            var urlParts = currentUrl.split('\/');\n                            urlParts[urlParts.length - 1] = href;\n                            currentUrl = urlParts.join('\/');\n                        }\n                        hasNextPage = true;\n                        return false; \/\/ break\n                    }\n                });\n\n                removeHTMLSafely($tempContainer);\n\n                if (!hasNextPage) {\n                    break;\n                }\n\n                pageIndex++;\n            }\n\n            \/\/ 合并所有分页内容\n            var finalContent = allContent.join('\\r\\n\\r\\n');\n\n            \/\/ 移除导航文本\n            finalContent = finalContent.replace(\/上一章\/g, '');\n            finalContent = finalContent.replace(\/目录\/g, '');\n            finalContent = finalContent.replace(\/下一页\/g, '');\n            finalContent = finalContent.replace(\/下一章\/g, '');\n            finalContent = finalContent.replace(\/首页\/g, '');\n\n            \/\/ 清理多余的空行\n            finalContent = finalContent.replace(\/\\r\\n\\r\\n\\r\\n+\/g, '\\r\\n\\r\\n').trim();\n\n            return finalContent;\n        } catch (e) {\n            flutterBridge.log(\"获取章节内容出错:\" + e.message);\n            return \"\";\n        }\n    }\n\n    \/\/ 发现页分类函数\n    async function getfinds() {\n        var sort = [\n            { \"title\": \"连载\", \"url\": \"https:\/\/cn.ttkan.co\/api\/nq\/amp_novel_list?type=lianzai&filter=*&page={{page}}&limit=18&language=cn&__amp_source_origin=https%3A%2F%2Fcn.ttkan.co\", \"type\": 0 },\n            { \"title\": \"玄幻\", \"url\": \"https:\/\/cn.ttkan.co\/api\/nq\/amp_novel_list?type=xuanhuan&filter=*&page={{page}}&limit=18&language=cn&__amp_source_origin=https%3A%2F%2Fcn.ttkan.co\", \"type\": 0 },\n            { \"title\": \"都市\", \"url\": \"https:\/\/cn.ttkan.co\/api\/nq\/amp_novel_list?type=dushi&filter=*&page={{page}}&limit=18&language=cn&__amp_source_origin=https%3A%2F%2Fcn.ttkan.co\", \"type\": 0 },\n            { \"title\": \"仙侠\", \"url\": \"https:\/\/cn.ttkan.co\/api\/nq\/amp_novel_list?type=wuxia&filter=*&page={{page}}&limit=18&language=cn&__amp_source_origin=https%3A%2F%2Fcn.ttkan.co\", \"type\": 0 },\n            { \"title\": \"言情\", \"url\": \"https:\/\/cn.ttkan.co\/api\/nq\/amp_novel_list?type=yanqing&filter=*&page={{page}}&limit=18&language=cn&__amp_source_origin=https%3A%2F%2Fcn.ttkan.co\", \"type\": 0 },\n            { \"title\": \"游戏\", \"url\": \"https:\/\/cn.ttkan.co\/api\/nq\/amp_novel_list?type=youxi&filter=*&page={{page}}&limit=18&language=cn&__amp_source_origin=https%3A%2F%2Fcn.ttkan.co\", \"type\": 0 },\n            { \"title\": \"科幻\", \"url\": \"https:\/\/cn.ttkan.co\/api\/nq\/amp_novel_list?type=kehuan&filter=*&page={{page}}&limit=18&language=cn&__amp_source_origin=https%3A%2F%2Fcn.ttkan.co\", \"type\": 0 },\n            { \"title\": \"悬疑\", \"url\": \"https:\/\/cn.ttkan.co\/api\/nq\/amp_novel_list?type=kongbu&filter=*&page={{page}}&limit=18&language=cn&__amp_source_origin=https%3A%2F%2Fcn.ttkan.co\", \"type\": 0 },\n            { \"title\": \"灵异\", \"url\": \"https:\/\/cn.ttkan.co\/api\/nq\/amp_novel_list?type=lingyi&filter=*&page={{page}}&limit=18&language=cn&__amp_source_origin=https%3A%2F%2Fcn.ttkan.co\", \"type\": 0 },\n            { \"title\": \"军事\", \"url\": \"https:\/\/cn.ttkan.co\/api\/nq\/amp_novel_list?type=lishi&filter=*&page={{page}}&limit=18&language=cn&__amp_source_origin=https%3A%2F%2Fcn.ttkan.co\", \"type\": 0 },\n            { \"title\": \"现言\", \"url\": \"https:\/\/cn.ttkan.co\/api\/nq\/amp_novel_list?type=tongren&filter=*&page={{page}}&limit=18&language=cn&__amp_source_origin=https%3A%2F%2Fcn.ttkan.co\", \"type\": 0 },\n            { \"title\": \"其它\", \"url\": \"https:\/\/cn.ttkan.co\/api\/nq\/amp_novel_list?type=qita&filter=*&page={{page}}&limit=18&language=cn&__amp_source_origin=https%3A%2F%2Fcn.ttkan.co\", \"type\": 0 }\n        ];\n        return JSON.stringify(sort);\n}\n\n\/\/ 发现页列表函数\n    async function parsefindList(html) {\n        var data = JSON.parse(html);\n        var books = [];\n        const bookUrlPrefix = \"https:\/\/cn.ttkan.co\/novel\/chapters\/\";\n        const coverUrlPrefix = \"https:\/\/static.ttkan.co\/cover\/\";\n        const coverUrlParams = \"?w=120&h=160&q=100\";\n\n        data.items.forEach(b => {\n            var book = {\n                \"bookUrl\": bookUrlPrefix + b[\"novel_id\"],\n                \"name\": b[\"name\"],\n                \"author\": b[\"author\"],\n                \"kind\": b[\"category\"],\n                \"coverUrl\": coverUrlPrefix + b[\"topic_img\"] + coverUrlParams,\n                \"intro\": b[\"description\"],\n                \"tocUrl\": bookUrlPrefix + b[\"novel_id\"],\n                \"wordCount\": b[\"wordCount\"],\n                \"type\": 0,\n                \"latestChapterTitle\": b[\"lastChapterTitle\"]\n            }\n            books.push(book);\n        })\n        return JSON.stringify(books);\n    }\n\n    \/\/ 发现页书籍列表函数\n    async function find(url, page) {\n        try {\n            \/\/ APP传入的URL包含{{page}}占位符,需要替换为实际页码\n            if (url.includes('{{page}}')) {\n                    url = url.replace('{{page}}', page);\n            }\n\n            var get = await http.Get(url, JSON.stringify(header), true);\n\n            return parsefindList(get.data);\n        } catch (e) {\n            flutterBridge.log(\"获取发现页出错:\" + e.message);\n            return \"[]\";\n        }\n    }\n\n    \/\/帮助页面\n        async function gethelp(){\n        return \"网站本身搜索就不能用所以没有写!!!知豆不?\";\n    }\n    \/\/ 导出函数供APP调用\n    window.search = search;\n    window.info = info;\n    window.chapter = chapter;\n    window.content = content;\n    window.getfinds = getfinds;\n    window.find = find;\n\n<\/script>\n\n<\/html>",
    "login": false,
    "lastUpdateTime": "1770977875659"
}
广告