[{"bookSourceUrl":"https:\/\/www.fushucun.com\/","bookSourceName":"福书网","enabledExplore":true,"enabled":true,"bookSourceGroup":"","author":"trae","help":false,"html":"<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>福书网<\/title>\n<\/head>\n<body>\n\n<\/body>\n<script src=\"https:\/\/vc.jd.com\/web\/js\/jquery-3.1.1.min.js\"><\/script>\n<script>\n  var isCookieJar=true;\n  class FlutterJSBridge {\n    constructor() {\n      this.init();\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    async CookieJar() {\n      try {\n        await window.flutter_inappwebview.callHandler('CookieJar', isCookieJar);\n      } catch (error) {\n        console.error('汇报完成准备失败:', error);\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    async getversion() {\n      try {\n        return await window.flutter_inappwebview.callHandler('version');\n      } catch (error) {\n        return \"0.0.0\";\n      }\n    }\n    \n    async htmlToText(str) {\n      try {\n          return await window.flutter_inappwebview.callHandler('htmlToText',str);\n        } catch (error) {\n          return \"\";\n      }\n    }\n    \n    async toTraditional(str) {\n      try {\n        return await window.flutter_inappwebview.callHandler('toTraditional',str);\n      } catch (error) {\n        return \"\";\n      }\n    }\n    \n    async toSimplified(str) {\n      try {\n        return await window.flutter_inappwebview.callHandler('toSimplified',str);\n      } catch (error) {\n        return \"\";\n      }\n    }\n\n    async voice() {\n      try {\n        return await window.flutter_inappwebview.callHandler('voice');\n      } catch (error) {\n        return \"\";\n      }\n    }\n   \n    async getDeviceid() {\n      try {\n        return await window.flutter_inappwebview.callHandler('id');\n      } catch (error) {\n        return \"\";\n      }\n    }\n\n    async getDevice() {\n      try {\n        return await window.flutter_inappwebview.callHandler('device');\n      } catch (error) {\n        return \"\";\n      }\n    }\n    \n    async getLoginUser() {\n      try {\n        return await window.flutter_inappwebview.callHandler('getLoginUser');\n      } catch (error) {\n        return \"\";\n      }\n    }\n\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    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    async showToast(str) {\n      try {\n        return await window.flutter_inappwebview.callHandler('showToast',str);\n      } catch (error) {\n        return false;\n      }\n    }\n    \n    async showLongToast(str) {\n      try {\n        return await window.flutter_inappwebview.callHandler('showLongToast',str);\n      } catch (error) {\n        return false;\n      }\n    }\n\n    async getWebViewUA() {\n      try {\n        return await window.flutter_inappwebview.callHandler('getWebViewUA');\n      } catch (error) {\n        return \"\";\n      }\n    }\n\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    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    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    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    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    async webViewGetAjax(url,html,body,header,ajaxregex) {\n      try {\n        return await window.flutter_inappwebview.callHandler('webviewajax',url,html,body,header,ajaxregex);\n      } catch (error) {\n        return \"\";\n      }\n    }\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    async startBrowserWithShouldOverrideUrlLoading(url,title,header) {\n      try {\n        return await window.flutter_inappwebview.callHandler('startBrowserWithShouldOverrideUrlLoading',url,title,header);\n      } catch (error) {\n        return \"\";\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    async back() {\n      try {\n        return await window.flutter_inappwebview.callHandler('back');\n      } catch (error) {\n        return false;\n      }\n    }\n\n    async utf8ToGbkUrlEncoded(str) {\n      try {\n        return await window.flutter_inappwebview.callHandler('utf8ToGbkUrlEncoded',str);\n      } catch (error) {\n        return \"\";\n      }\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    async addbook(bookUrl) {\n      try {\n        return await window.flutter_inappwebview.callHandler('addbook',bookUrl);\n      } catch (error) {\n        return \"\";\n      }\n    }\n    \n    \n    async getdurChapterIndex(bookUrl) {\n      try {\n        return await window.flutter_inappwebview.callHandler('getdurChapterIndex',bookUrl);\n      } catch (error) {\n        return 0;\n      }\n    }\n    \n    async base64encode(str) {\n      try {\n        return await window.flutter_inappwebview.callHandler('base64encode',str);\n      } catch (error) {\n        return \"\";\n      }\n    }\n    \n    async base64decode(str) {\n      try {\n        return await window.flutter_inappwebview.callHandler('base64decode',str);\n      } catch (error) {\n        return \"\";\n      }\n    }\n    \n\n  }\n\n  class Http {\n    constructor() {\n      this.open = false;\n      this.requestTimestamps = [];\n      this.rateLimit = 5;\n      this.rateLimitWindow = 1000;\n    }\n\n    async checkRateLimit() {\n      if(!this.open) return;\n      const now = Date.now();\n      this.requestTimestamps = this.requestTimestamps.filter(timestamp => now - timestamp < this.rateLimitWindow);\n      if (this.requestTimestamps.length >= this.rateLimit) {\n        const oldestTimestamp = this.requestTimestamps[0];\n        const waitTime = this.rateLimitWindow - (now - oldestTimestamp);\n        await new Promise(resolve => setTimeout(resolve, waitTime));\n        return this.checkRateLimit();\n      }\n      this.requestTimestamps.push(now);\n    }\n\n    async Get(url,headers,followRedirects) {\n      try {\n        await this.checkRateLimit();\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        await this.checkRateLimit();\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        await this.checkRateLimit();\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    async getLoginInfo(){\n      return await this.get(\"LoginInfo\")\n    }\n\n    async putLoginInfo(info){\n      return await this.set(\"LoginInfo\",info)\n    }\n   \n    async getbookVariable(bookurl){\n      return await this.get(bookurl)\n    }\n    \n    async setbookVariable(bookurl,value){\n      return await this.set(bookurl,value)\n    }\n  }\n\n  class Cookie {\n    constructor() {}\n\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    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    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    async setCookie(url,key,value) {\n      try {\n        return await window.flutter_inappwebview.callHandler('cookie.setcookie',url,key,value);\n      } catch (error) {\n        return null;\n      }\n    }\n\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  function parseHTMLSafely(htmlStr) {\n    try {\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  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  function removeHTMLTags(htmlString) {\n    let result = htmlString.replace(\/<script\\b[^<]*(?:(?!<\\\/script>)<[^<]*)*<\\\/script>\/gi, '');\n    result = result.replace(\/<style\\b[^<]*(?:(?!<\\\/style>)<[^<]*)*<\\\/style>\/gi, '');\n    return result;\n  }\n\n<\/script>\n\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:\/\/www.fushucun.com\"\n    var header={\n        \"User-Agent\": \"Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/142.0.0.0 Safari\/537.36\",\n        \"Referer\": baseurl,\n        \"Accept\": \"text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/avif,image\/webp,*\/*;q=0.8\",\n        \"Accept-Language\": \"zh-CN,zh;q=0.9,en;q=0.8\"\n    };\n\n    \/\/ 智能容器定位工具函数\n    function findBestContainer($htmlContainer, linkMatcher, minMatchCount) {\n        var $bestContainer = null;\n        var maxMatchCount = 0;\n        var checkedParents = new Set();\n        \n        $htmlContainer.find(\"a\").each(function() {\n            var href = $(this).attr('href');\n            \n            if (!linkMatcher(href)) {\n                return true;\n            }\n            \n            var $parent = $(this).parent();\n            var depth = 0;\n            \n            while ($parent.length > 0 && depth < 10) {\n                var parentKey = $parent[0].outerHTML.substring(0, Math.min(500, $parent[0].outerHTML.length));\n                \n                if (!checkedParents.has(parentKey)) {\n                    checkedParents.add(parentKey);\n                    \n                    var count = $parent.find(\"a\").filter(function() {\n                        return linkMatcher($(this).attr('href'));\n                    }).length;\n                    \n                    if (count > maxMatchCount && count >= minMatchCount) {\n                        maxMatchCount = count;\n                        $bestContainer = $parent;\n                    }\n                }\n                \n                $parent = $parent.parent();\n                depth++;\n            }\n        });\n        \n        return $bestContainer;\n    }\n\n    function normalizeUrl(url) {\n        if (!url) return \"\";\n        if (url.startsWith('http')) return url;\n        if (url.startsWith('\/')) return baseurl + url;\n        return baseurl + '\/' + url;\n    }\n\n    \/*\n     * search(key, page) - 搜索功能\n     * 福书网使用帝国CMS的POST搜索接口\n     * 接口: \/e\/search\/index.php (POST)\n     * 参数: keyboard, show, classid\n     *\/\n    async function search(key, page) {\n        if(page > 1){\n            return \"[]\";\n        }\n\n        var searchUrl = baseurl + \"\/e\/search\/index.php\";\n        var body = \"keyboard=\" + await flutterBridge.utf8ToGbkUrlEncoded(key) + \"&show=title&tempid=1&classid=2,6&x=0&y=0\";\n\n        flutterBridge.log(\"搜索URL: \" + searchUrl);\n        flutterBridge.log(\"搜索参数: \" + body);\n\n        \/\/ 使用 http.Post 发送搜索请求 (302重定向到结果页)\n        \/\/ Header 必须与浏览器抓包一致\n        var postHeader = {\n            \"Connection\": \"keep-alive\",\n            \"sec-ch-ua\": '\"Chromium\";v=\"147\", \"Not:A-Brand\";v=\"8\", \"Google Chrome\";v=\"147\"',\n            \"sec-ch-ua-mobile\": \"?0\",\n            \"sec-ch-ua-platform\": '\"macOS\"',\n            \"Upgrade-Insecure-Requests\": \"1\",\n            \"User-Agent\": \"Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/147.0.0.0 Safari\/537.36\",\n            \"Origin\": baseurl,\n            \"Accept\": \"text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/avif,image\/webp,*\/*;q=0.8\",\n            \"Sec-Fetch-Site\": \"same-origin\",\n            \"Sec-Fetch-Mode\": \"navigate\",\n            \"Sec-Fetch-User\": \"?1\",\n            \"Sec-Fetch-Dest\": \"document\",\n            \"Referer\": baseurl,\n            \"Cache-Control\":\"no-cache\",\n        };\n        var get = await http.Post(searchUrl, postHeader, body, \"application\/x-www-form-urlencoded\", true);\n        \n        \/\/ 获取返回的数据\n        var htmlContent = get.data || \"\";\n\n\n        if(!htmlContent){\n            flutterBridge.showToast(\"搜索请求失败\");\n            return \"[]\";\n        }\n\n        flutterBridge.text(0, htmlContent);\n\n        var books = [];\n        var processedUrls = new Set();\n        var $tempContainer = parseHTMLSafely(removeHTMLTags(htmlContent));\n\n        \/\/ 智能定位搜索结果容器 (article-list)\n        var linkMatcher = function(href) {\n            if (!href) return false;\n            \/\/ 福书网的书籍链接格式: \/book\/数字.html\n            return \/\\\/book\\\/\\d+\\.html\/.test(href);\n        };\n        \n        var $resultContainer = findBestContainer($tempContainer, linkMatcher, 1);\n\n        if (!$resultContainer) {\n            $resultContainer = $tempContainer;\n        }\n\n        \/\/ 提取书籍信息\n        $resultContainer.find(\"a\").each(function() {\n            try{\n                var bookUrl = $(this).attr('href');\n                \n                if (!bookUrl || processedUrls.has(bookUrl)) {\n                    return true;\n                }\n                \n                if (!linkMatcher(bookUrl)) {\n                    return true;\n                }\n                \n                processedUrls.add(bookUrl);\n                \n                \/\/ 提取文本，格式: 书名——作者\n                var fullText = $(this).text().trim();\n                \n                \/\/ 解析书名、作者\n                var bookName = fullText;\n                var author = \"\";\n                var kind = \"\";\n                \n                \/\/ 提取作者（——后面）\n                var authorMatch = fullText.match(\/——(.+)$\/);\n                if(authorMatch) {\n                    author = authorMatch[1].trim();\n                    bookName = fullText.substring(0, fullText.indexOf('——')).trim();\n                }\n                \n                if(bookName && bookName.length > 1){\n                    bookUrl = normalizeUrl(bookUrl);\n\n                    var book={\n                        \"bookUrl\": bookUrl,\n                        \"name\": bookName,\n                        \"author\": author,\n                        \"kind\": kind,\n                        \"coverUrl\": \"\",\n                        \"intro\": \"\",\n                        \"tocUrl\": bookUrl,\n                        \"wordCount\": \"\",\n                        \"type\": 0,\n                        \"latestChapterTitle\": \"\"\n                    };\n                    books.push(book);\n                }\n            }catch(e){\n                flutterBridge.log(\"解析书籍信息出错: \" + e.message);\n            }\n        });\n\n        removeHTMLSafely($tempContainer);\n        flutterBridge.log(\"找到 \" + books.length + \" 本书\");\n        return JSON.stringify(books);\n    }\n\n    \/*\n     * info(bookurl) - 书籍详情\n     * 访问书籍页面提取基本信息\n     *\/\n    async function info(bookurl) {\n        var mheader={\n            ...header,\n            \"Referer\": baseurl\n        };\n\n        var htmlContent = await flutterBridge.webview(bookurl, \"\", \"\", \"\", JSON.stringify(mheader));\n\n        if(!htmlContent){\n            flutterBridge.showToast(\"获取信息失败\");\n            return JSON.stringify({});\n        }\n\n        flutterBridge.text(1, htmlContent);\n\n        var $tempContainer = parseHTMLSafely(removeHTMLTags(htmlContent));\n\n        \/\/ 从h1提取标题，格式: 书名（分类）——作者\n        var h1Text = $tempContainer.find(\"h1\").first().text().trim();\n        \n        var name = \"\";\n        var author = \"\";\n        var kind = \"\";\n        var intro = \"\";\n        var latestChapterTitle = \"\";\n\n        \/\/ 解析h1文本\n        if(h1Text) {\n            name = h1Text;\n            \n            \/\/ 提取分类\n            var categoryMatch = h1Text.match(\/（([^）]+)）\/);\n            if(categoryMatch) {\n                kind = categoryMatch[1];\n                name = h1Text.substring(0, h1Text.indexOf('（')).trim();\n            }\n            \n            \/\/ 提取作者\n            var authorMatch = h1Text.match(\/——(.+)$\/);\n            if(authorMatch) {\n                author = authorMatch[1].trim();\n            }\n        }\n\n        \/\/ 尝试从其他位置找作者\n        if(!author) {\n            $tempContainer.find(\"*\").each(function() {\n                var text = $(this).text().trim();\n                if(text.indexOf(\"作者\") !== -1 && !author) {\n                    var match = text.match(\/作者[：:\\s]*(.+?)(?:$|[\\r\\n])\/);\n                    if(match) {\n                        author = match[1].trim();\n                    }\n                }\n            });\n        }\n\n        \/\/ 提取简介（查找\"文案：\"后面的内容，限制长度）\n        var foundIntro = false;\n        $tempContainer.find(\"*\").each(function() {\n            if(foundIntro) return false;\n            \n            var $elem = $(this);\n            var text = $elem.text().trim();\n            \n            \/\/ 查找包含\"文案\"的元素\n            if(text.indexOf(\"文案\") !== -1 && text.indexOf(\"文案\") < 100) {\n                var idx = text.indexOf(\"文案\");\n                var potentialIntro = text.substring(idx + 2).replace(\/[：:]\\s*\/, \"\").trim();\n                \n                \/\/ 简介应该在200字以内\n                if(potentialIntro.length > 10 && potentialIntro.length < 500) {\n                    intro = potentialIntro;\n                    foundIntro = true;\n                    return false;\n                }\n                \n                \/\/ 如果太长，可能是包含了正文，截取前面部分\n                if(potentialIntro.length >= 500) {\n                    \/\/ 找到第一个句号或换行处截断\n                    var cutIdx = potentialIntro.indexOf('。');\n                    if(cutIdx > 20 && cutIdx < 300) {\n                        intro = potentialIntro.substring(0, cutIdx + 1).trim();\n                        foundIntro = true;\n                        return false;\n                    }\n                }\n            }\n        });\n        \n        \/\/ 如果还没找到，尝试从特定位置提取（h1后面第一个大段文本）\n        if(!intro && !foundIntro) {\n            var h1Elem = $tempContainer.find(\"h1\").first();\n            if(h1Elem.length > 0) {\n                \/\/ 获取h1后面的兄弟元素或父元素的下一个子元素\n                var nextElements = h1Elem.parent().find(\"p, div\").slice(0, 5);\n                nextElements.each(function() {\n                    if(intro) return false;\n                    \n                    var text = $(this).text().trim();\n                    \/\/ 排除导航、按钮等\n                    if(text.length > 30 && text.length < 500 &&\n                       text.indexOf(\"首页\") === -1 &&\n                       text.indexOf(\"返回\") === -1 &&\n                       text.indexOf(\"加入收藏\") === -1 &&\n                       text.indexOf(\"来顶一下\") === -1) {\n                        intro = text;\n                    }\n                });\n            }\n        }\n\n        \/\/ 提取最新章节（从下拉菜单或分页信息）\n        var pageInfo = $tempContainer.text();\n        var pageMatch = pageInfo.match(\/(\\d+)\\s*\\\/\\s*(\\d+)\/);\n        if(pageMatch) {\n            latestChapterTitle = \"第\" + pageMatch[2] + \"章\";\n        }\n\n        var book={\n            \"bookUrl\": bookurl,\n            \"name\": name,\n            \"author\": author,\n            \"kind\": kind,\n            \"coverUrl\": \"\",\n            \"intro\": intro,\n            \"tocUrl\": bookurl,\n            \"wordCount\": \"\",\n            \"type\": 0,\n            \"latestChapterTitle\": latestChapterTitle\n        };\n\n        removeHTMLSafely($tempContainer);\n        return JSON.stringify(book);\n    }\n\n    \/*\n     * chapter(tocUrl) - 章节列表\n     * 从下拉菜单或分页链接中提取章节\n     *\/\n    async function chapter(tocUrl) {\n        var mheader={\n            ...header,\n            \"Referer\": baseurl\n        };\n\n        var htmlContent = await flutterBridge.webview(tocUrl, \"\", \"\", \"\", JSON.stringify(mheader));\n\n        if(!htmlContent){\n            flutterBridge.showToast(\"获取目录失败\");\n            return \"[]\";\n        }\n\n        flutterBridge.text(2, htmlContent);\n\n        var chapters = [];\n        var $tempContainer = parseHTMLSafely(removeHTMLTags(htmlContent));\n\n        \/\/ 方法1: 从下拉菜单提取章节\n        var totalChapters = 0;\n        \n        \/\/ 查找分页信息获取总章节数\n        var pageInfo = $tempContainer.text();\n        var pageMatch = pageInfo.match(\/(\\d+)\\s*\\\/\\s*(\\d+)\/);\n        if(pageMatch) {\n            totalChapters = parseInt(pageMatch[2]);\n        }\n\n        \/\/ 如果找到了总章节数，生成章节列表\n        if(totalChapters > 0) {\n            for(var i = 1; i <= totalChapters; i++) {\n                chapters.push({\n                    \"name\": \"第\" + i + \"章\",\n                    \"chapterId\": tocUrl + \"?\" + i,\n                    \"index\": i - 1,\n                    \"isPay\": false,\n                    \"isVip\": false,\n                    \"isVolume\": false,\n                    \"tag\": \"\"\n                });\n            }\n        } else {\n            \/\/ 方法2: 从下拉菜单选项提取\n            $tempContainer.find(\"select option\").each(function(index) {\n                if(index === 0) return; \/\/ 跳过第一个（通常是\"请选择\"）\n                \n                var chapterName = $(this).text().trim();\n                var chapterUrl = $(this).val() || (tocUrl + \"?\" + index);\n                \n                if(chapterName) {\n                    chapters.push({\n                        \"name\": chapterName,\n                        \"chapterId\": normalizeUrl(chapterUrl),\n                        \"index\": index - 1,\n                        \"isPay\": false,\n                        \"isVip\": false,\n                        \"isVolume\": false,\n                        \"tag\": \"\"\n                    });\n                }\n            });\n        }\n\n        removeHTMLSafely($tempContainer);\n        flutterBridge.log(\"找到 \" + chapters.length + \" 个章节\");\n        return JSON.stringify(chapters);\n    }\n\n    \/*\n     * content(url) - 章节内容\n     * 访问 ?N 参数获取对应章节内容\n     *\/\n    async function content(url) {\n        var mheader={\n            ...header,\n            \"Referer\": baseurl\n        };\n\n        var htmlContent = await flutterBridge.webview(url, \"\", \"\", \"\", JSON.stringify(mheader));\n\n        if(!htmlContent){\n            flutterBridge.showToast(\"获取内容失败\");\n            return \"\";\n        }\n\n        flutterBridge.text(3, htmlContent);\n\n        var $tempContainer = parseHTMLSafely(removeHTMLTags(htmlContent));\n        var contenttxt = \"\";\n\n        \/\/ 查找正文内容区域\n        \/\/ 福书网页面结构：正文在 <main> 或 <td class=\"main\"> 容器内\n        \n        \/\/ 方法1: 直接定位 main 容器\n        var $mainContainer = $tempContainer.find(\".main\").first();\n        \n        if($mainContainer.length > 0) {\n            \/\/ 从 main 中提取文本\n            var mainText = $mainContainer.text().trim();\n            \n            if(mainText) {\n                contenttxt = mainText;\n                \n                \/\/ 清理内容\n                \/\/ 移除网站宣传语（包括后面的所有内容）\n                contenttxt = contenttxt.replace(\/最最最全的耽美文库.*$\/s, \"\");\n                contenttxt = contenttxt.replace(\/一秒钟记住我们的网址.*$\/s, \"\");\n                contenttxt = contenttxt.replace(\/www\\.fushucun\\.com.*$\/s, \"\");\n                \n                \/\/ 移除元数据标签行\n                contenttxt = contenttxt.replace(\/^(作品来源|作者|时间|文案)[^\\n]*\\n?\/gm, \"\");\n                contenttxt = contenttxt.replace(\/^.*(主角|配角|其它|一句话简介|立意)[：:][^\\n]*$\/gm, \"\");\n                \n                \/\/ 移除多余的空白行\n                contenttxt = contenttxt.replace(\/\\n{3,}\/g, \"\\n\\n\");\n                contenttxt = contenttxt.trim();\n                \n                \/\/ 限制最大长度\n                if(contenttxt.length > 20000) {\n                    contenttxt = contenttxt.substring(0, 20000);\n                }\n            }\n        }\n\n        \/\/ 兜底方案\n        if(!contenttxt || contenttxt.length < 50){\n            flutterBridge.showToast(\"获取内容失败\");\n            return \"\";\n        }\n\n        removeHTMLSafely($tempContainer);\n\n        if(!contenttxt || contenttxt.length < 50){\n            flutterBridge.showToast(\"获取内容失败\");\n            return \"\";\n        }\n\n        return contenttxt;\n    }\n\n    \/*\n     * getfinds() - 发现\/分类\n     * 返回福书网的分类列表\n     *\/\n    async function getfinds() {\n        var result = [];\n        var push = (title, url, type) => result.push({\n            title: title,\n            url: url,\n            type: type || 0\n        });\n\n        push(\"穿越重生\", \"\/chuanyuechongsheng\/\", 0);\n        push(\"古代架空\", \"\/gudaixiaoshuo\/\", 0);\n        push(\"GL百合\", \"\/baihexiaoshuo\/\", 0);\n        push(\"玄幻网游\", \"\/xuanhuanwangyou\/\", 0);\n        push(\"现代都市\", \"\/xiandaixiaoshuo\/\", 0);\n        push(\"同人BL\", \"\/tongrenxiaoshuo\/\", 0);\n        push(\"本站推荐\", \"\/best\/\", 0);\n\n        return JSON.stringify(result);\n    }\n\n    \/*\n     * find(url, page) - 分类浏览\n     * 浏览某个分类的书籍列表\n     *\/\n    async function find(url, page) {\n        var u = url.replaceAll(\"{{page}}\", page);\n        if(page > 1) {\n            u = u.replace(\/\\\/$\/, \"\") + \"\/index_\" + page + \".html\";\n        }\n        if(!u.startsWith('http')){\n            u = baseurl + u;\n        }\n\n        flutterBridge.log(\"分类URL: \" + u);\n\n        var mheader={\n            ...header,\n            \"Referer\": baseurl\n        };\n\n        var htmlContent = await flutterBridge.webview(u, \"\", \"\", \"\", JSON.stringify(mheader));\n\n        if(!htmlContent){\n            flutterBridge.showToast(\"获取分类失败\");\n            return \"[]\";\n        }\n\n        flutterBridge.text(0, htmlContent);\n\n        var books = [];\n        var processedUrls = new Set();\n        var $tempContainer = parseHTMLSafely(removeHTMLTags(htmlContent));\n\n        \/\/ 智能定位书籍列表容器\n        var linkMatcher = function(href) {\n            if (!href) return false;\n            \/\/ 福书网的书籍链接格式: \/book\/数字.html 或 \/年份\/数字.html\n            return \/\\\/book\\\/\\d+\\.html\/.test(href) || \/\\d{4}\\\/\\d+\\.html\/.test(href);\n        };\n        \n        var $resultContainer = findBestContainer($tempContainer, linkMatcher, 1);\n\n        if (!$resultContainer) {\n            $resultContainer = $tempContainer;\n        }\n\n        \/\/ 提取书籍（复用搜索逻辑）\n        $resultContainer.find(\"a\").each(function() {\n            try{\n                var bookUrl = $(this).attr('href');\n                \n                if (!bookUrl || processedUrls.has(bookUrl)) {\n                    return true;\n                }\n                \n                if (!linkMatcher(bookUrl)) {\n                    return true;\n                }\n                \n                processedUrls.add(bookUrl);\n                \n                var fullText = $(this).text().trim();\n                var bookName = fullText;\n                var author = \"\";\n                var kind = \"\";\n                \n                var categoryMatch = fullText.match(\/（([^）]+)）\/);\n                if(categoryMatch) {\n                    kind = categoryMatch[1];\n                    bookName = fullText.substring(0, fullText.indexOf('（')).trim();\n                }\n                \n                var authorMatch = fullText.match(\/——(.+?)(?:\\s|$)\/);\n                if(authorMatch) {\n                    author = authorMatch[1].trim();\n                }\n                \n                var dateIndex = bookName.lastIndexOf(\"20\");\n                if(dateIndex > 10 && dateIndex < bookName.length - 4) {\n                    bookName = bookName.substring(0, dateIndex).trim();\n                }\n                \n                if(bookName && bookName.length > 1){\n                    bookUrl = normalizeUrl(bookUrl);\n\n                    var book={\n                        \"bookUrl\": bookUrl,\n                        \"name\": bookName,\n                        \"author\": author,\n                        \"kind\": kind,\n                        \"coverUrl\": \"\",\n                        \"intro\": \"\",\n                        \"tocUrl\": bookUrl,\n                        \"wordCount\": \"\",\n                        \"type\": 0,\n                        \"latestChapterTitle\": \"\"\n                    };\n                    books.push(book);\n                }\n            }catch(e){\n                flutterBridge.log(\"解析书籍信息出错: \" + e.message);\n            }\n        });\n\n        removeHTMLSafely($tempContainer);\n        flutterBridge.log(\"找到 \" + books.length + \" 本书\");\n        return JSON.stringify(books);\n    }\n\n    async function getloginurl(){\n        return baseurl;\n    }\n\n<\/script>\n<\/html>\n","login":true,"lastUpdateTime":"1778188807032"}]