Files
legado-source-debugger/1764576348.json
2025-12-02 10:37:22 +08:00

246 lines
126 KiB
JSON
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
[
{
"bookSourceComment": "漫画发布页https://wn01.link/\n1⃣无需翻墙国内直连\n2⃣漫画没加载出来尝试刷新一下\n3⃣脸黑🌚刷不出来的别用wifi用流量看。",
"bookSourceGroup": "漫画",
"bookSourceName": "绅士漫画( 优+++ )",
"bookSourceType": 2,
"bookSourceUrl": "https://www.wnacg.ru/",
"customOrder": 472,
"enabled": true,
"enabledCookieJar": true,
"enabledExplore": true,
"exploreUrl": "[\n {\n \"title\": \"🔖排行🔖\",\n \"url\": \"\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n },\n {\n \"title\": \"月榜\",\n \"url\": \"https://www.wnacg.ru/albums-favorite_ranking-page-{{page}}-type-month.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.2\n }\n },\n {\n \"title\": \"周榜\",\n \"url\": \"https://www.wnacg.ru/albums-favorite_ranking-page-{{page}}-type-week.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.2\n }\n },\n {\n \"title\": \"日榜\",\n \"url\": \"https://www.wnacg.ru/albums-favorite_ranking-page-{{page}}-type-day.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.2\n }\n },\n {\n \"title\": \"🔖同人志🔖\",\n \"url\": \"\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n },\n {\n \"title\": \"创建时间\",\n \"url\": \"https://www.wnacg.ru/albums-index-page-{{page}}-cate-1.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.2\n }\n },\n {\n \"title\": \"上传时间\",\n \"url\": \"https://www.wnacg.ru/albums-index-page-{{page}}-cate-1.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.2\n }\n },\n {\n \"title\": \"图片数\",\n \"url\": \"https://www.wnacg.ru/albums-index-page-{{page}}-cate-1.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.2\n }\n },\n {\n \"title\": \"🔖韩漫🔖\",\n \"url\": \"\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n },\n {\n \"title\": \"创建时间\",\n \"url\": \"https://www.wnacg.ru/albums-index-page-{{page}}-cate-20.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.2\n }\n },\n {\n \"title\": \"上传时间\",\n \"url\": \"https://www.wnacg.ru/albums-index-page-{{page}}-cate-20.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.2\n }\n },\n {\n \"title\": \"图片数\",\n \"url\": \"https://www.wnacg.ru/albums-index-page-{{page}}-cate-20.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.2\n }\n },\n\n {\n \"title\": \"🔖单行本🔖\",\n \"url\": \"\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n },\n {\n \"title\": \"创建时间\",\n \"url\": \"https://www.wnacg.ru/albums-index-page-{{page}}-cate-9.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.2\n }\n },\n {\n \"title\": \"上传时间\",\n \"url\": \"https://www.wnacg.ru/albums-index-page-{{page}}-cate-9.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.2\n }\n },\n {\n \"title\": \"图片数\",\n \"url\": \"https://www.wnacg.ru/albums-index-page-{{page}}-cate-9.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.2\n }\n },\n {\n \"title\": \"🔖杂志&短篇🔖\",\n \"url\": \"\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n },\n {\n \"title\": \"创建时间\",\n \"url\": \"https://www.wnacg.ru/albums-index-page-{{page}}-cate-10.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.2\n }\n },\n {\n \"title\": \"上传时间\",\n \"url\": \"https://www.wnacg.ru/albums-index-page-{{page}}-cate-10.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.2\n }\n },\n {\n \"title\": \"图片数\",\n \"url\": \"https://www.wnacg.ru/albums-index-page-{{page}}-cate-10.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.2\n }\n }\n]",
"header": "@js:\nJSON.stringify({\n\"Referer\":baseUrl\n})",
"lastUpdateTime": "1763999238954",
"respondTime": 180000,
"ruleBookInfo": {
"coverUrl": "//div[@class='asTB']/div[@class='asTBcell uwthumb']/img/@src##////##https://",
"downloadUrls": "//div[@id='ads']/a[1]/@href",
"intro": "//div[@class='asTBcell uwconn']/p/text()@js:result.split('\\n').join('')",
"kind": "//div[@class='addtags']/a[@class='tagshow']/text()",
"wordCount": "//div[@class='asTBcell uwconn']/label/text()@js:\nconst infoSplit = result.split('\\n')\nconst info = []\nfor(var i = 0;i<infoSplit .length;i++){\ninfo.push(infoSplit[i].split('')[1])\n}\ninfo.join(' ')"
},
"ruleContent": {
"content": "//span[@id='imgarea']/a/img/@src##//##https://@js:\n\n// 获取前缀URL\nconst regexStr = \"^https://img5\\\\.qy0\\\\.ru/data/\\\\d+/\\\\d+/\";\nconst regex = new RegExp(regexStr, \"g\");\nconst prefixUrlMatch = result.match(regex);\nconst prefixUrl = prefixUrlMatch ? prefixUrlMatch[0] : \"\";\n\n// 解析图片信息数组(容错:数组为空时设为[]\nconst imgInfoList = JSON.parse(java.get('imgInfoList') || '[]');\n\n// 拼接完整图片链接直接使用原始imgName\nconst imgUrlList = [];\nif (prefixUrl) {\n imgInfoList.forEach(item => {\n // 仅做空值容错不修改imgName原始值\n const imgName = item.imgName || '';\n const imgExtension = item.imgExtension || 'jpg';\n const fullImgUrl = prefixUrl + imgName + \".\" + imgExtension;\n imgUrlList.push(fullImgUrl);\n });\n}\n\n// 规则输出拼接后的img标签\nimgUrlList.map(x=>'<img src=\\\"'+x+'\\\">')\n.join(\"\\n\")",
"imageStyle": "FULL"
},
"ruleExplore": [],
"ruleSearch": {
"bookList": "//div[@class='gallary_wrap']/ul/li",
"bookUrl": "//div[@class='info']/div[@class='title']/a/@href,{\"webView\":true}",
"checkKeyWord": "元魔法",
"coverUrl": "//li/div[1]/a/img/@src",
"intro": "//li/div[@class='info']/div[@class='info_col']/text()@js:\nvar timeStr = result.split(\"\")[1] || \"\";\nvar updateTime = timeStr.match(/創建於(\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2})/)?.[1] || \"无时间\";\n'更新时间: ' + updateTime;",
"kind": "//li/div[1]/@class@js:\nif (result[0].indexOf('cate-1') !== -1) { \n '同人志/汉化'; // 加return否则无返回值\n} else if (result[0].indexOf('cate-12') !== -1) {\n '同人志/日语'; \n} else if (result[0].indexOf('cate-9') !== -1) {\n '单行本/汉化';\n} else if (result[0].indexOf('cate-13') !== -1) {\n '单行本/日语';\n} else if (result[0].indexOf('cate-10') !== -1) {\n '杂志&短篇/汉化';\n} else if (result[0].indexOf('cate-14') !== -1) {\n '杂志&短篇/日语';\n} else if (result[0].indexOf('cate-18') !== -1) {\n '杂志&短篇/English';\n} else if (result[0].indexOf('cate-20') !== -1) {\n '韩漫/汉化';\n} else if (result[0].indexOf('cate-21') !== -1) {\n '韩漫/生肉';\n} else {\n ''; \n}",
"name": "//div[@class='info']/div[@class='title']/a//text()@js:\nconst bookname= result.split('\\n').join('').replace(/^.*?]/g, '')\nbookname",
"wordCount": "//li/div[@class='info']/div[@class='info_col']/text()@js:\nvar imgNum = result.split(\"\")[0].match(/\\d+(?=張圖片)/g)[0]\nimgNum+'页'"
},
"ruleToc": {
"chapterList": "//div[@class='gallary_wrap tb']/ul/li[1]@js:\nvar imgInfoList = [];\nvar nextPageUrl = [];\n\n// 1. 处理分页URL保留有效逻辑\nvar rawHrefs = java.getString(\"//div[@class='f_left paginator']/a/@href\") || \"\";\nnextPageUrl = rawHrefs.split('\\n').filter(x => x.trim());\nnextPageUrl = nextPageUrl.map(x => {\n var url = x.trim();\n while (url.startsWith(\"/\")) {\n url = url.slice(1);\n }\n return url.startsWith(\"http\") ? url : \"https://www.wnacg.ru/\" + url;\n});\nnextPageUrl.unshift(baseUrl);\n\n// 2. 核心工具函数预处理HTML移除注释+清理空格)\nfunction cleanHtml(html) {\n if (!html) return \"\";\n // 步骤1移除所有HTML注释<!-- ... -->),彻底消除干扰\n html = html.replace(/<!--[\\s\\S]*?-->/g, \"\");\n // 步骤2移除多余空白字符换行、制表符等简化匹配\n html = html.replace(/\\s+/g, \" \");\n return html;\n}\n\n// 3. 核心工具函数提取单页的“序号数组”和“图片src数组”\nfunction extractPageData(cleanedHtml) {\n var names = []; // 序号数组001、002、003...\n var srcs = []; // 图片src数组与序号一一对应\n\n // 提取所有序号(<span class=\"name tb\">xxx</span>\n var nameRegex = /<span class=\"name tb\">([\\d]+)<\\/span>/g;\n var nameMatch;\n while ((nameMatch = nameRegex.exec(cleanedHtml)) !== null) {\n names.push(nameMatch[1]);\n }\n\n // 提取所有有效图片src与序号对应的imgalt包含“_序号”\n var srcRegex = /<img src=\"([^\"]+)\" alt=\"[^\"]+_([\\d]+)\"/g;\n var srcMatch;\n while ((srcMatch = srcRegex.exec(cleanedHtml)) !== null) {\n // 确保src对应的序号在names数组中避免无关图片\n if (names.includes(srcMatch[2])) {\n srcs.push(srcMatch[1]);\n }\n }\n\n return { names: names, srcs: srcs };\n}\n\n// 4. 执行多页提取\ntry {\n for (var p = 0; p < nextPageUrl.length; p++) {\n var pageUrl = nextPageUrl[p];\n var pageNum = p + 1;\n\n // 步骤1获取并清理HTML移除注释+多余空格)\n var rawHtml = new String(java.ajax(pageUrl)) || \"\";\n var cleanedHtml = cleanHtml(rawHtml);\n\n // 步骤2提取当前页的序号和src数组\n var pageData = extractPageData(cleanedHtml);\n var pageNames = pageData.names;\n var pageSrcs = pageData.srcs;\n\n // 步骤3按索引配对序号和src一一对应避免漏配\n var addedNum = 0;\n for (var i = 0; i < pageNames.length; i++) {\n var imgName = pageNames[i];\n var imgSrc = pageSrcs[i] || \"\";\n var imgExtension = \"\";\n\n // 提取扩展名\n if (imgSrc) {\n var dotIndex = imgSrc.lastIndexOf(\".\");\n if (dotIndex !== -1) {\n imgExtension = imgSrc.slice(dotIndex + 1).toLowerCase().split(/[?#]/)[0];\n }\n }\n\n // 去重并添加到数组\n if (imgName && imgExtension && !imgInfoList.some(item => item.imgName === imgName)) {\n imgInfoList.push({ imgName: imgName, imgExtension: imgExtension });\n addedNum++;\n }\n }\n\n }\n} catch (e) {\n java.log(\"提取报错:\" + e.message);\n}\n\nimgInfoList.sort((a, b) => parseInt(a.imgName) - parseInt(b.imgName));\n\n// 返回原结果,不影响目录列表\njava.put('imgInfoList',JSON.stringify(imgInfoList))\nresult;",
"chapterName": "//li/text()@js:'全话阅读'",
"chapterUrl": "//li//a/@href,{\"webView\":true}",
"updateTime": "//li/div[@class='info']/div[@class='info_col']/text()"
},
"searchUrl": "https://www.wnacg.ru/search/?q={{key}}&f=_all&s=create_time_DESC&syn=yes",
"weight": 500
},
{
"bookSourceName": "飘天(梯子 模糊搜索)",
"bookSourceType": 0,
"bookSourceUrl": "https://www.piaotia.com",
"customOrder": 0,
"enabled": true,
"enabledCookieJar": false,
"enabledExplore": true,
"lastUpdateTime": "1764413976626",
"respondTime": 30000,
"ruleBookInfo": {
"intro": "tr:nth-child(1) > td:nth-child(2) > div@text",
"tocUrl": "caption > a@href"
},
"ruleContent": {
"content": "@js:result=result.match(/style4.js[\\s\\S]+<br>([\\s\\S]+)<\\/div>[\\s\\S]+!-- 翻页上\\/)[1]\n"
},
"ruleExplore": [],
"ruleSearch": {
"author": ".odd:nth-child(3)@text",
"bookList": "tr:nth-child(n+2)",
"bookUrl": ".odd > a@href",
"kind": ".even:nth-child(6)@text",
"lastChapter": ".even > a@text",
"name": ".odd > a@text",
"wordCount": ".even:nth-child(4)@text@js:String(parseInt(result.replace('k',''))*1000)"
},
"ruleToc": {
"chapterList": ".centent ul li",
"chapterName": "a@text",
"chapterUrl": "a@href"
},
"searchUrl": "/modules/article/search.php?searchkey={{java.encodeURI(key,'gbk')}}&searchtype=articlename",
"weight": 0
},
{
"bookSourceComment": "更新日志请点击登录,更新书源中查看\n\n兼容正式版阅读app",
"bookSourceGroup": "大灰狼聚合",
"bookSourceName": "🍅大灰狼融合4.11.25(完全版)",
"bookSourceType": 0,
"bookSourceUrl": "大灰狼融合4.0",
"bookUrlPattern": "https?:\\/\\/(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z0-9-]+(?::\\d+)?\\/detail.*",
"customOrder": 22,
"enabled": true,
"enabledCookieJar": false,
"enabledExplore": true,
"exploreUrl": "<js>\nfunction setArgument(key, value) {\n var open_argument = source.getVariable();\n open_argument = getArguments(open_argument, '') || {};\n open_argument[key] = value;\n var result = JSON.stringify(open_argument);\n source.setVariable(result);\n return result;\n}\n\nvar open_argument = source.getVariable();\nvar base_url = getArguments(open_argument, 'server') || '';\nvar ms = getArguments(open_argument, 'tone_id') || '';\nvar source_type = getArguments(open_argument, 'source_type') || '男频';\nvar tab = getArguments(open_argument, 'media') || '小说';\nvar sources = getArguments(open_argument, 'source') || '番茄';\n\n\nvar sdtoken;\ntry {\n var loginInfoMap = source.getLoginInfoMap ? source.getLoginInfoMap() : {};\n sdtoken = String(loginInfoMap['手动填写番茄token(可不填)'] || '');\n} catch (e) {\n sdtoken = '';\n}\n\nvar rawCookie = getFanqieCookie() || sdtoken;\nvar match = rawCookie.match(/sessionid=[^;]+/);\nvar fqcookie = match ? match[0] : '';\nvar fqssionid = '';\nif (!fqcookie) {\n java.toast('您还未登陆番茄账号,无法同步数据哦!');\n} else {\n fqssionid = getSessionId(fqcookie)\n}\nvar fqsjurl = base_url + \"/bookshelf?page={{page}}&ssionid=\" + fqssionid;\nvar fqtjurl = base_url + \"/fqrecommend?page={{page}}&ssionid=\" + fqssionid;\nvar fqlsurl = base_url + \"/fqhistory?page={{page}}&ssionid=\" + fqssionid;\n\nvar groupDatas = [];\nvar infoData = [];\n\nfunction deviceType() {\n try {\n return !!java.androidId();\n } catch (e) {\n return false;\n }\n}\n\nvar hasValidCookie = fqcookie.length > 0;\n\nif (hasValidCookie) {\n function groupQuery() {\n try {\n var url = base_url + \"/group_name?ssionid=\" + fqssionid;\n var res = java.ajax(url);\n var response = JSON.parse(res);\n\n if (!(response && response.data)) {}\n\n response.data.forEach(function(group) {\n var keys = Object.keys(group);\n if (keys.length > 0) {\n var key = keys[0];\n var value = group[key];\n if (value && value.length) {\n var option = {\n \"method\": \"POST\",\n \"body\": {\n \"book_ids\": value,\n \"page\": \"{{page}}\"\n }\n };\n groupDatas.push({\n title: key,\n url: base_url + \"/bookshelf,\" + JSON.stringify(option),\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 0.45\n }\n });\n }\n }\n });\n\n if (groupDatas.length % 2 !== 0) {\n groupDatas.push({\n title: \"--\",\n url: \"\",\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 0.45\n }\n });\n }\n } catch (e) {\n java.longToast(\"番茄登录过期,已隐藏番茄书架\");\n }\n }\n\n try {\n java.longToast(\"正在加载分组数据...\");\n var userUrl = base_url + \"/fquser?ssionid=\" + fqssionid;\n var userRes = java.ajax(userUrl);\n var userData = JSON.parse(userRes);\n\n var userName = (userData && userData.data && userData.data.name) ? userData.data.name : '未知用户';\n if (!userName.includes('未知用户')) {\n infoData = [{\n title: userName + \"个人中心\",\n url: fqsjurl,\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 1\n }\n }, {\n title: \"个性推荐(番茄)\",\n url: fqtjurl,\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 0.45\n }\n }, {\n title: \"历史阅读(番茄)\",\n url: fqlsurl,\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 0.45\n }\n }];\n }\n groupQuery();\n } catch (e) {\n java.longToast(\"番茄登录过期,已隐藏番茄书架\");\n }\n}\n\nvar style_list = [];\ntry {\n var durl = `${base_url}/discovestyle?source=${sources}&source_type=${source_type}&tab=${tab}`;\n var res = java.ajax(durl);\n var result = JSON.parse(res);\n style_list = result.data || [];\n if (result.msg) {\n java.toast(result.msg);\n }\n} catch (e) {\n java.toast(\"发现样式获取失败\");\n}\n\nvar finalData = infoData.concat(groupDatas, style_list);\nJSON.stringify(finalData);\n</js>",
"header": "{ \"User-Agent\":\"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Mobile Safari/537.36 Edg/138.0.0.0\" }",
"jsLib": "var host = [\n 'http://219.154.201.122:5006'\n];\n\nfunction getArguments(open_argument, key) {\n try {\n open_argument = JSON.parse(open_argument);\n } catch (e) {\n open_argument = {\n media: \"小说\",\n tone_id: \"默认音色\",\n server: host[0],\n source: \"全部\",\n source_type: \"男频\",\n };\n }\n if (key) {\n return open_argument[key];\n } else {\n return open_argument;\n }\n}\n\nfunction setArguments(key, value) {\n const {\n source\n } = this;\n let open_argument = source.getVariable();\n open_argument = getArguments(open_argument, '');\n open_argument[key] = value;\n open_argument = JSON.stringify(open_argument);\n source.setVariable(open_argument);\n return open_argument;\n}\n\nfunction decrypt(Text) {\n return Text;\n}\n\nfunction paraForAndroid(content, sources) {\n let {\n java,\n cache,\n source\n } = this;\n let plcolor = getArguments(source.getVariable(), \"plcolor\");\n if (!plcolor) {\n plcolor = \"#000000\";\n }\n\n const createSvg = this.createSvg.bind(this);\n\n return content.replace(/<p>(.*?)(?:<comment ident=\"([^\"]*)\" count=\"([^\"]*)\" \\/>)?<\\/p>/g,\n (match, text, url, count) => {\n if (url && count) {\n const click = 0;\n cache.putMemory(url, click);\n const encodedUrl = url;\n return `<p>${text}<img src=\"${createSvg(count, plcolor,encodedUrl,sources)}\"></p>`;\n } else {\n return `<p>${text}</p>`;\n }\n }\n );\n}\n\n\nfunction showCmt(url, sources) {\n let {\n java,\n cache\n } = this;\n\n const currentTime = Date.now();\n const click = cache.getFromMemory(url);\n let isqread = false;\n try {\n java.qread();\n isqread = true;\n } catch (e) {}\n if (click < 1 && !isqread) {\n cache.putMemory(url, click + 1);\n return;\n } else {\n try {\n java.startBrowserDp(url, sources + '段评');\n } catch (e) {\n java.startBrowser(url, sources + '段评');\n }\n }\n}\n\n\n\nfunction createSvg(number, color, encodedUrl, sources) {\n var displayText = number > 99 ? \"99+\" : number.toString();\n var loginInfoMap = {};\n\n if (this.source && typeof this.source.getLoginInfoMap == 'function') {\n loginInfoMap = this.source.getLoginInfoMap() || {};\n }\n\n var bubbleStyle = String(loginInfoMap['段评气泡样式'] || '0');\n var svg;\n\n // 样式1精致圆形 - 简洁优雅\n if (bubbleStyle == '1') {\n svg = '<svg width=\"1000\" height=\"1000\" xmlns=\"http://www.w3.org/2000/svg\">' +\n '<circle cx=\"500\" cy=\"500\" r=\"440\" fill=\"none\" stroke=\"' + color + '\" stroke-width=\"32\" opacity=\"0.2\"/>' +\n '<circle cx=\"500\" cy=\"500\" r=\"440\" fill=\"none\" stroke=\"' + color + '\" stroke-width=\"4\"/>' +\n '<text x=\"500\" y=\"500\" font-family=\"-apple-system, sans-serif\" text-anchor=\"middle\" ' +\n 'font-size=\"400\" fill=\"' + color + '\" dy=\"0.35em\" font-weight=\"500\">' + displayText + '</text>' +\n '</svg>';\n }\n // 样式2微信风格 - 熟悉亲切的对话气泡\n else if (bubbleStyle == '2') {\n svg = '<svg width=\"1000\" height=\"900\" xmlns=\"http://www.w3.org/2000/svg\">' +\n '<path d=\"M120,120 L880,120 Q920,120 920,160 L920,680 Q920,720 880,720 L280,720 L120,840 L120,720 Q80,720 80,680 L80,160 Q80,120 120,120 Z\" ' +\n 'fill=\"none\" stroke=\"' + color + '\" stroke-width=\"24\" stroke-linejoin=\"round\"/>' +\n '<text x=\"500\" y=\"440\" font-family=\"-apple-system, sans-serif\" text-anchor=\"middle\" ' +\n 'font-size=\"360\" fill=\"' + color + '\" dy=\"0.32em\" font-weight=\"500\">' + displayText + '</text>' +\n '</svg>';\n }\n // 样式3悬浮标签 - 扁平现代风格\n else if (bubbleStyle == '3') {\n svg = '<svg width=\"1000\" height=\"600\" xmlns=\"http://www.w3.org/2000/svg\">' +\n '<rect x=\"80\" y=\"80\" width=\"840\" height=\"440\" rx=\"220\" ry=\"220\" ' +\n 'fill=\"none\" stroke=\"' + color + '\" stroke-width=\"6\"/>' +\n '<rect x=\"100\" y=\"100\" width=\"800\" height=\"400\" rx=\"200\" ry=\"200\" ' +\n 'fill=\"none\" stroke=\"' + color + '\" stroke-width=\"20\" opacity=\"0.3\"/>' +\n '<text x=\"500\" y=\"310\" font-family=\"-apple-system, sans-serif\" text-anchor=\"middle\" ' +\n 'font-size=\"280\" fill=\"' + color + '\" dy=\"0.32em\" font-weight=\"500\" letter-spacing=\"8\">' + displayText + '</text>' +\n '</svg>';\n }\n // 样式4书签标注 - 符合阅读场景\n else if (bubbleStyle == '4') {\n svg = '<svg width=\"900\" height=\"1000\" xmlns=\"http://www.w3.org/2000/svg\">' +\n '<path d=\"M150,80 L750,80 Q800,80 800,130 L800,870 Q800,920 750,920 L480,920 L450,980 L420,920 L150,920 Q100,920 100,870 L100,130 Q100,80 150,80 Z\" ' +\n 'fill=\"none\" stroke=\"' + color + '\" stroke-width=\"24\" stroke-linejoin=\"round\"/>' +\n '<line x1=\"150\" y1=\"280\" x2=\"750\" y2=\"280\" stroke=\"' + color + '\" stroke-width=\"3\" opacity=\"0.3\"/>' +\n '<text x=\"450\" y=\"520\" font-family=\"Georgia, serif\" text-anchor=\"middle\" ' +\n 'font-size=\"360\" fill=\"' + color + '\" dy=\"0.32em\" font-weight=\"400\">' + displayText + '</text>' +\n '<text x=\"450\" y=\"800\" font-family=\"-apple-system, sans-serif\" text-anchor=\"middle\" ' +\n 'font-size=\"70\" fill=\"' + color + '\" opacity=\"0.6\" letter-spacing=\"3\">评论</text>' +\n '</svg>';\n }\n // 默认返回样式0\n else {\n svg = '<svg width=\"160\" height=\"120\" xmlns=\"http://www.w3.org/2000/svg\">' +\n '<path d=\"M 55 10 ' +\n 'L 120 10 ' +\n 'Q 150 10 150 40 ' +\n 'L 150 80 ' +\n 'Q 150 110 120 110 ' +\n 'L 55 110 ' +\n 'Q 25 110 25 80 ' +\n 'L 25 75 ' +\n 'L 3 60 ' +\n 'L 25 45 ' +\n 'L 25 40 ' +\n 'Q 25 10 55 10 Z\" ' +\n 'fill=\"none\" ' +\n 'stroke=\"' + color + '\" ' +\n 'stroke-width=\"2\" ' +\n 'stroke-linejoin=\"round\"/>' +\n '<!-- 数字文本 -->' +\n '<text x=\"87\" y=\"75\" ' +\n 'font-family=\"Arial, sans-serif\" ' +\n 'text-anchor=\"middle\" ' +\n 'dominant-baseline=\"middle\" ' +\n 'font-size=\"50\" ' +\n 'font-weight=\"bold\" ' +\n 'fill=\"' + color + '\">' +\n displayText +\n '</text>' +\n '<!-- 提示文本 -->' +\n '<text x=\"87\" y=\"95\" ' +\n 'font-family=\"Arial, sans-serif\" ' +\n 'text-anchor=\"middle\" ' +\n 'dominant-baseline=\"middle\" ' +\n 'font-size=\"9\" ' +\n 'fill=\"' + color + '\" ' +\n 'opacity=\"0.7\">' +\n '刷新查看' +\n '</text>' +\n '</svg>';\n }\n\n var encodedSvg = this.java.base64Encode(svg);\n return 'data:image/svg+xml;base64,' + encodedSvg + ',{\"js\":\"showCmt(\\'' + encodedUrl + '\\', \\'' + sources + '\\')\",\"style\":\"text\"}';\n}\n\n\n\nfunction cleanHTML(html) {\n let result = html\n .replace(/<header[^>]*>[\\s\\S]*?<\\/header>/gi, \"\")\n .replace(/<div class=\"tt-title\"[^>]*>[\\s\\S]*?<\\/div>/gi, \"\")\n .replace(/<(?!\\/?p\\b|\\/?img\\b)[^>]+>/gi, \"\");\n result = result.replace(/<\\/?p[^>]*>/g, \"\\n\");\n return result.replace(/\\n+/g, \"\\n\").trim();\n}\n\nfunction getSessionId(cookieString) {\n const match = cookieString.match(/sessionid=([^;]+)/);\n return match ? match[1] : null;\n}\n\nfunction getKey(key) {\n let parts = key.split(\";\");\n for (let part of parts) {\n if (part.includes(\"qttoken\")) {\n return part.split(\"=\")[1];\n }\n }\n return \"\";\n}\n\nfunction getFanqieCookie() {\n const {\n cookie\n } = this;\n try {\n return String(cookie.getCookie('fanqienovel.com') || java.getCookie('fanqienovel.com') || '');\n } catch (e) {\n return '';\n }\n}\n\n\nfunction paraForiOS(html, sources) {\n return html.replace(\n /<p>(.*?)(?:<comment ident=\"([^\"]*)\" count=\"([^\"]*)\" \\/>)?<\\/p>/g,\n function(match, text, url, count) {\n if (url && count) {\n const encodedUrl = url.replace(/&/g, '&amp;');\n return `<span rs-native>${text}<comment count=\"${count}\" onPress=\"java.showReadingBrowser('${encodedUrl}','${sources}段评')\"></span>`;\n } else {\n return `<span rs-native>${text}</span>`;\n }\n }\n );\n}",
"lastUpdateTime": "1764434902238",
"loginUi": "[{\n \"name\": \"邮箱\",\n \"type\": \"text\"\n },\n {\n \"name\": \"密码\",\n \"type\": \"password\"\n },\n {\n \"name\": \"♥登录书源\",\n \"type\": \"button\",\n \"action\": \"login(true)\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"🍅番茄登录\",\n \"type\": \"button\",\n \"action\": \"fq_login()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n },\n {\n \"name\": \"🏝用户后台/注册\",\n \"type\": \"button\",\n \"action\": \"loginqt()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n },\n {\n \"name\": \" 🔚 退出登录\",\n \"type\": \"button\",\n \"action\": \"logout()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"🔮 检测登录\",\n \"type\": \"button\",\n \"action\": \"checkStatus()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n\n }, {\n \"name\": \" 🗑 清除设备\",\n \"type\": \"button\",\n \"action\": \"clearDevice()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n\n }, {\n \"name\": \"📑更少简介\",\n \"type\": \"button\",\n \"action\": \"set_info()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"📝段评开关\",\n \"type\": \"button\",\n \"action\": \"paracomment('fqpara')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"👨‍🦱男频发现\",\n \"type\": \"button\",\n \"action\": \"set_source_type('男频')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"👩‍🦰女频发现\",\n \"type\": \"button\",\n \"action\": \"set_source_type('女频')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"☕打赏享福利\",\n \"type\": \"button\",\n \"action\": \"vip()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"❇️ 更新书源\",\n \"type\": \"button\",\n \"action\": \"renderVersionPage()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"🗂当前模式\",\n \"type\": \"button\",\n \"action\": \"get_media()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n }, {\n \"name\": \"📖小说模式\",\n \"type\": \"button\",\n \"action\": \"set_media('小说')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"🔊听书模式\",\n \"type\": \"button\",\n \"action\": \"set_media('听书')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"🏞漫画模式\",\n \"type\": \"button\",\n \"action\": \"set_media('漫画')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"🖲短剧模式\",\n \"type\": \"button\",\n \"action\": \"set_media('短剧')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"🎚切换服务器\",\n \"type\": \"button\",\n \"action\": \"set_server()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"♻️检测当前服务器\",\n \"type\": \"button\",\n \"action\": \"checkNet()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"⚕️本地/服务器 \",\n \"type\": \"button\",\n \"action\": \"get_proxy()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"📌永久发布页📌\",\n \"type\": \"button\",\n \"action\": \"api()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"↓↓下方可切换来源用于搜索/发现页↓↓\",\n \"type\": \"button\",\n \"action\": \"get_media()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n }, {\n \"name\": \"✨️网友推荐\",\n \"type\": \"button\",\n \"action\": \"set_source('推荐')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"💖我来推荐\",\n \"type\": \"button\",\n \"action\": \"put_book()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"全部\",\n \"type\": \"button\",\n \"action\": \"set_source('全部')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('番茄')\",\n 'name': '番茄',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('69书吧')\",\n 'name': '69书吧',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('七猫')\",\n 'name': '七猫',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('百度')\",\n 'name': '百度',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('起点')\",\n 'name': '起点(第三方)',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('得间')\",\n 'name': '得间',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('塔读')\",\n 'name': '塔读',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('书旗')\",\n 'name': '书旗',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('QQ')\",\n 'name': 'QQ',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('猫眼')\",\n 'name': '猫眼',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('搜书神器')\",\n 'name': '搜书神器',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('得奇')\",\n 'name': '得奇',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('伪69')\",\n 'name': '伪69',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('爱下电子书')\",\n 'name': '爱下电子书',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('小米')\",\n 'name': '小米',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('星星小说')\",\n 'name': '星星小说',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('笔趣阁22')\",\n 'name': '笔趣阁22',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('顶点')\",\n 'name': '顶点',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('幻梦轻小说')\",\n 'name': '幻梦轻小说',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('江湖')\",\n 'name': '江湖',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('歪瑞古德')\",\n 'name': '歪瑞古德漫画',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('河马')\",\n 'name': '河马短剧',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('知乎')\",\n 'name': '知乎',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('vip')\",\n 'name': '下方为VIP专属书源(点击此处搜所有vip)',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n }, {\n 'action': \"set_source('喜马拉雅')\",\n 'name': '喜马拉雅',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('酷我')\",\n 'name': '酷我',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('svip')\",\n 'name': '下方为SVIP专属书源(点击此处搜所有svip)',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n }, {\n 'action': \"set_source('超会专属短剧')\",\n 'name': '超会专属短剧',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('超会专属小说')\",\n 'name': '超会专属小说',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('台湾小说')\",\n 'name': '台湾小说',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('3A小说')\",\n 'name': '3A小说',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('包子漫画')\",\n 'name': '包子漫画',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, { \t \t\n 'action': \"set_source('QQ阅读')\",\n 'name': 'QQ(会员书籍免费)',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('书香之家')\",\n 'name': '书香之家',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \t 'action': \"set_source('追更人')\",\n 'name': '追更人',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('漫画屋')\",\n 'name': '漫画屋',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('漫画网')\",\n 'name': '漫画网',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('海洋听书')\",\n 'name': '海洋听书',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('鹿鹿')\",\n 'name': '鹿鹿',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('完本小说')\",\n 'name': '完本小说',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('盐选文库')\",\n 'name': '盐选文库',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, { \t \t\n 'action': \"set_source('福利小说')\",\n 'name': '福利小说',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('轻之文库')\",\n 'name': '轻之文库',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('小说三千')\",\n 'name': '小说三千',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('阅友小说')\",\n 'name': '阅友小说',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('星空小说')\",\n 'name': '星空小说',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('全免漫画')\",\n 'name': '全免漫画',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('365小说')\",\n 'name': '365小说',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('福利漫画')\",\n 'name': '福利漫画',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('万相书城')\",\n 'name': '万相书城',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('独步小说')\",\n 'name': '独步小说',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('铅笔轻小说')\",\n 'name': '铅笔轻小说',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('沧元图')\",\n 'name': '沧元图',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('全本同人')\",\n 'name': '全本同人',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('毒舌影视')\",\n 'name': '毒舌影视',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"自定义搜索源(多个用英文,分割)\",\n \"type\": \"text\"\n }, {\n \"name\": \"听书Ai音色填写后点击右上角✔\",\n \"type\": \"text\"\n }, {\n \"name\": \"自定义服务器(可不填)\",\n \"type\": \"text\"\n }, {\n \"name\": \"自定义评论颜色(可不填)\",\n \"type\": \"text\"\n }, {\n \"name\": \"段评气泡样式\",\n \"type\": \"text\"\n }, {\n \"name\": \"手动填写番茄token(可不填)\",\n \"type\": \"text\"\n }\n]",
"loginUrl": "// 当前书源版本号,切勿修改,否则影响更新的识别\nconst localVersion = '4.11.25';\n\nfunction login(flag) {\n if (flag == undefined) {\n result = JSON.parse(source.getLoginInfo())\n } else {\n java.longToast(\"\\n\\n💞正在登录中...\")\n putLoginInfo(JSON.stringify(result))\n }\n let base_url = getArgument('server')\n let zdyserver = String(result['自定义服务器(可不填)']);\n if (zdyserver.includes('http')) {\n setArgument('server', zdyserver);\n if (getKey(String(cookie.getCookie(base_url)))) {\n let cookies = cookie.getCookie(base_url)\ntry{cookie.removeCookie(base_url)} catch(e){cookie.removeCookie(base_url)}\n cookie.setCookie(zdyserver, cookies)\n }\n java.toast(`\\n\\n当前服务器为自定义服务器\\n${zdyserver}\\n\\n切换服务器请先清空服务器地址中的数据`);\n }\n let zdytone_id = String(result['听书Ai音色填写后点击右上角✔'] || '');\n if (zdytone_id) {\n setArgument('tone_id', zdytone_id);\n } else {\n setArgument('tone_id', '默认音色');\n }\n let plcolor = String(result['自定义评论颜色(可不填)'] || '#000000');\n if (plcolor) {\n setArgument('plcolor', plcolor);\n } else {\n setArgument('plcolor', '#000000');\n }\n let zdysources = String(result['自定义搜索源(多个用英文,分割)'] || '');\n if (zdysources) {\n setArgument('source', zdysources);\n };\n base_url = getArgument('server')\n let register_email = String(result['邮箱'])\n let password = String(result['密码'])\n let key = String(result['密钥'] || '')\n\n //java.log(cookie.getCookie(base_url))\n if ((register_email && password || key) && !String(getKey(String(cookie.getCookie(base_url))))) {\n try{cookie.removeCookie(base_url)} catch(e){cookie.removeCookie(base_url)}\n let deviceKey = '';\n try {\n deviceKey = java.deviceID();\n } catch (e) {\n deviceKey = java.androidId();\n };\n let deviceId = deviceKey;\n if (register_email && password) {\n let options = JSON.stringify({\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n register_email: result['邮箱'],\n password: result['密码']\n })\n })\n try {\n let data = JSON.parse(java.ajax(`${base_url}/login_api,${options}`))\n if (data.code == 0) {\n //java.toast(deviceId)\n java.toast(\"\\n\\n✅登录成功\")\n cookie.setCookie(base_url, `qttoken=${data.key};deviceId=${deviceId}`)\n result['密钥'] = data.key\n putLoginInfo(JSON.stringify(result))\n } else {\n java.toast('\\n\\n💔' + data.msg || \"登录失败,请重试!\")\n }\n } catch (e) {\n java.toast(\"\\n\\n💔登录失败请重试\\n\" + e.message)\n }\n } else {\n cookie.setCookie(base_url, `qttoken=${key};deviceId=${deviceId}`)\n let res = java.ajax(`${base_url}/user_api,{\"method\":\"POST\",\"headers\":{\"cookie\":\"${cookie.getCookie(base_url)}\"}}`)\n try {\n res = JSON.parse(res)\n if (res.id != undefined) {\n java.toast('\\n\\n密钥登录成功')\n result['邮箱'] = res.email\n putLoginInfo(JSON.stringify(result))\n } else {\n throw new Error()\n }\n } catch (e) {\n java.log(e)\n java.toast(\"\\n\\n💔登录失败\")\n }\n }\n } else if (flag && String(getKey(String(cookie.getCookie(base_url))))) {\n java.toast(\"\\n\\n当前✅已登录请🚫退出登录后重新登录\");\n //checkStatus();\n } else if (flag) {\n java.toast(\"\\n\\n⛔请先填写邮箱和密码\");\n }\n}\n\n// 检测服务器\nfunction checkNet() {\n let url = getArgument('server');\n java.longToast(`\\n\\n♻正在检测${url}\\n请稍等~`);\n let date1 = new Date().getTime();\n let html = java.ajax(url + '/login');\n let date2 = new Date().getTime();\n let t = date2 - date1;\n let c = String(html).indexOf('大灰狼');\n let code = 1;\n let time = t / 1000 + 's';\n let logTime = '【' + url + '】\\n┋┋\\n' + '解析时间:' + time;\n if (c == -1 || t > 5000) {\n java.longToast('\\n💔【访问失败提示】\\n' + '┏┅━┅━┅━┅━┅┅━┅━┅┓\\n┋┋\\n' + logTime + '\\n┋┋\\n♣当前接口无法访问(可能被墙)♣️\\n┋┋\\n请切换其他接口/切换网络环境\\n┋┋' + '\\n┗┅━┅━┅━┅━┅┅━┅━┅┛');\n } else if (t < 1000) {\n java.longToast('\\n💖【网络环境优良】\\n' + '┏┅━┅━┅━┅━┅┅━┅━┅┓\\n┋┋\\n' + logTime + '\\n┋┋\\n❤延迟低推荐使用此接口❤\\n┋┋\\n网络环境优良请继续保持状态\\n┋┋' + '\\n┗┅━┅━┅━┅━┅┅━┅━┅┛');\n } else if (t >= 1000 && t < 2000) {\n java.longToast('\\n💛【网络环境一般】\\n' + '┏┅━┅━┅━┅━┅┅━┅━┅┓\\n┋┋\\n' + logTime + '\\n┋┋\\n♦延迟一般勉强可使用♦\\n┋┋\\n请切换其他接口或切换网络环境\\n┋┋' + '\\n┗┅━┅━┅━┅━┅┅━┅━┅┛');\n } else if (t >= 2000 && t < 5000) {\n java.longToast('\\n💔【网络环境堪忧】\\n' + '┏┅━┅━┅━┅━┅┅━┅━┅┓\\n┋┋\\n' + logTime + '\\n┋┋\\n♠延迟过高不建议使用♠\\n┋┋\\n请切换其他接口或切换网络环境\\n┋┋' + '\\n┗┅━┅━┅━┅━┅┅━┅━┅┛');\n }\n}\n\nfunction isVips(res) {\n let isVIP = '';\n let vipEndTime = res.vip_end_time;\n let formattedDate = '';\n\n if (vipEndTime && vipEndTime !== 0) {\n let date = new Date(vipEndTime * 1000);\n formattedDate = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;\n let currentTime = Math.floor(Date.now() / 1000);\n let remainingDays = Math.ceil((vipEndTime - currentTime) / (24 * 60 * 60));\n if (currentTime > vipEndTime) {\n isVIP = `${res.vip_level === 1 ? \"VIP\" : \"SVIP\"} (已过期)`;\n } else if (remainingDays <= 7) {\n isVIP = `${res.vip_level === 1 ? \"VIP\" : \"SVIP\"} 剩余${remainingDays}天`;\n } else {\n if (vipEndTime < 1912946812) {\n isVIP = `${res.vip_level === 1 ? \"VIP\" : \"SVIP\"}(${formattedDate})`;\n } else {\n isVIP = `${res.vip_level === 1 ? \"VIP\" : \"SVIP\"} (永久)`;\n }\n }\n } else {\n isVIP = '您尚未开通VIP';\n }\n return isVIP;\n}\n\nfunction checkStatus() {\n java.longToast('\\n\\n♻检测中...');\n let base_url = getArgument('server')\n let res = java.ajax(`${base_url}/user_api,{\"method\":\"POST\",\"headers\":{\"cookie\":\"${cookie.getCookie(base_url)}\"}}`)\n try {\n res = JSON.parse(res)\n if (res.id != undefined) {\n result['邮箱'] = res.email\n putLoginInfo(JSON.stringify(result))\n let devices\n try {\n devices = JSON.parse(res.device).length;\n } catch (e) {\n devices = res.device ? 1 : 0;\n }\n let isVip = isVips(res);\n tips = `\n┏┅┅┅┅┅┅┱┄┄┄┄┄┄┄┄┄┄┐\n  🧢昵称     ${res.nickname.padEnd(20,\"\\t\") || \"未设置\".padEnd(20,\"\\t\")}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n 邮箱    ${res.email.replace(/(.{3}).*?@/,\"$1***@\").padEnd(20,\"\\t\")}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n 🔑密钥    ${(`${res.user_key.substring(0,4)}***${res.user_key.slice(-4)}`).padEnd(20,\"\\t\")}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n 📅注册时间  ${java.timeFormat(res.register_time*1000).padEnd(20,\"\\t\")}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n 🗒今日阅读  ${(java.timeFormat(new Date()).slice(0,10)==java.timeFormat(res.last_read_time * 1000).slice(0,10)?res.day_read_count:0).toString().padEnd(20,\"\\t\")}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n 📚累计阅读  ${res.all_read_count.toString().padEnd(20,\"\\t\")}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n 🕓最后阅读  ${(res.last_read_time != 0?java.timeFormat(res.last_read_time * 1000):'未阅读').padEnd(20,\"\\t\")}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n 📱关联设备  ${devices.toString().padEnd(20,\"\\t\")}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n 👑会员状态  ${isVip.padEnd(20,\"\\t\")}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n 🚫封禁状态  ${res.is_banned?'已封禁':'正常 '}       \n┗┅┅┅┅┅┅┹┄┄┄┄┄┄┄┄┄┄┘\n`\n java.log(tips)\n java.longToast(tips)\n } else {\n throw new Error(res.msg)\n }\n } catch (e) {\n //java.log(e)\n java.toast(\"\\n检测登录失败\\n\" + e.message)\n }\n}\n\nfunction clearDevice() {\n let base_url = getArgument('server')\n let res = java.ajax(`${base_url}/clear,{\"method\":\"POST\",\"headers\":{\"cookie\":\"${cookie.getCookie(base_url)}\"}}`)\n java.toast(res.code === 0 ? \"\\n\\n📴设备清除成功\" : res.msg)\n Packages.java.lang.Thread.sleep(500)\n checkStatus()\n}\n// 保存登录UI信息\nfunction putLoginInfo(info) {\n try {\n let key = java.androidId()\n let encodeStr = Packages.android.util.Base64.encodeToString(java.createSymmetricCrypto(\"AES\", key).encrypt(info), 2)\n cache.put(`userInfo_${source.getKey()}`, encodeStr)\n return true\n } catch (e) {\n java.log(e)\n return source.putLoginInfo(info)\n }\n}\n\n// 填写密钥\nfunction loginqt() {\n java.startBrowserAwait(getArgument('server') + '/login', '登录大灰狼小说书源');\n}\n\n//登录番茄\nfunction fq_login() {\n try {\n java.startBrowserAwait(\"https://fanqienovel.com/\", \"登录\")\n } catch (e) {\n java.toast(e)\n }\n try {\n cookie.removeCookie(\"snssdk.com\")\n } catch (e) {}\n var cookie_ = \"sessionid=\" + (String(cookie.getKey(\"fanqienovel.com\", \"sessionid\")) ? String(cookie.getKey(\"fanqienovel.com\", \"sessionid\")) : source.getLoginInfoMap()['手动登录Token'])\n let user\n try {\n user = JSON.parse(java.ajax(\"https://fanqienovel.com/api/user/info/v2,\" + JSON.stringify({\n method: \"GET\",\n headers: {\n \"Cookie\": cookie_\n }\n }))).data.name\n } catch (e) {\n java.log(e)\n }\n if (!cookie_ || cookie_ == \"sessionid=\" || !user) {\n java.toast(\"未获取到登录凭据,登录失败\")\n return false\n }\n java.toast(\"\\n\\n欢迎 \" + user + \"\\n登录成功\")\n return true\n}\n\n//退出登录\nfunction logout() {\n cookie.removeCookie(\"fanqienovel.com\");\n cookie.removeCookie(\"snssdk.com\");\n cookie.removeCookie(\"69shuba.com\");\n /*\n let servers = host\n for (let server of servers) {\n \ttry{cookie.removeCookie(server )} catch(e){cookie.removeCookie(server )}\n }\n */\ntry{cookie.removeCookie(getArgument('server'))} catch(e){cookie.removeCookie(getArgument('server'))}\n java.toast(\"退出登录成功\");\n}\n\n//获取参数\nfunction getArgument(key) {\n let open_argument = source.getVariable();\n open_argument = getArguments(open_argument, '');\n return open_argument[key];\n}\n\n//设置参数\nfunction setArgument(key, value) {\n let open_argument = source.getVariable();\n open_argument = getArguments(open_argument, '');\n open_argument[key] = value;\n open_argument = JSON.stringify(open_argument);\n source.setVariable(open_argument);\n return open_argument;\n}\n\n// 设置本地or云端访问\nfunction get_proxy() {\n let proxy = getArgument('proxy');\n if (proxy == '本地') {\n setArgument('proxy', '云端');\n java.longToast('\\n所有数据采用\\n\\n服务器网络访问\\n\\n如果发现用不了请切换本地网络访问如69书吧');\n } else {\n setArgument('proxy', '本地');\n java.longToast('\\n所有数据采用\\n\\n本地网络访问\\n\\n如果发现用不了请开启网络代理如69书吧');\n }\n}\n\n\n//设置男女频\nfunction set_source_type(source_type) {\n setArgument(\"source_type\", source_type);\n java.toast(\"\\n发现页已设置为:\" + source_type);\n}\n\n//首页\nfunction api() {\n java.startBrowserAwait('http://fb.langge.cf', \"首页\");\n}\n\n//打赏\nfunction vip() {\n java.startBrowserAwait(getArgument('server') + '/coffee', \"喝咖啡\");\n}\n\nvar server = getArgument('server');\n\n//设置搜索媒体\nfunction set_media(media) {\n const mediaConfig = {\n '喜马拉雅': ['听书'],\n '番茄': '*',\n '福利小说':['小说'],\n '全免漫画':['漫画'],\n '包子漫画':['漫画'],\n '漫画网':['漫画'],\n '风车漫画':['漫画'],\n '福利漫画':['漫画'],\n '好看漫画':['漫画'],\n '酷我':['听书'],\n '海洋听书':['听书'],\n '七猫': ['小说', '听书'],\n '河马': ['短剧'],\n '超会专属短剧': ['短剧'],\n '歪瑞古德': ['漫画'],\n '毒舌影视':['短剧'],\n '全部': '*', // 允许所有模式\n '默认': ['小说']\n };\n\n const source = getArgument('source');\n const allowedModes = mediaConfig[source] || mediaConfig['默认'];\n let targetMedia = mediaConfig['默认'][0];\n let isAllowed = false;\n\n if (allowedModes === '*' || allowedModes.includes(media)) {\n targetMedia = media;\n isAllowed = true;\n } else if (Array.isArray(allowedModes)) {\n targetMedia = allowedModes[0];\n }\n\n const message = isAllowed ?\n `\\n\\n已切换至${targetMedia}\\n请重新搜索书籍` :\n `\\n\\n目前${source}:不支持【${media}】模式!\\n已自动切换至${targetMedia}`;\n\n setArgument('media', targetMedia);\n java.toast(message);\n}\n\n//获取搜索媒体\nfunction get_media() {\n let media = getArgument('media');\n let source = getArgument('source');\n if (media == '') {\n media = '全部';\n }\n var tishi = '\\n\\n当前服务器' + getArgument('server')\n java.longToast(`\\n\\n当前使用源${source}-${media}${tishi}`);\n}\n\n\n//设置服务器\nfunction set_server() {\n putLoginInfo(JSON.stringify(result))\n let zdyserver;\n let base_url = getArgument('server')\n try {\n zdyserver = String(result['自定义服务器(可不填)']);\n if (zdyserver.includes('http')) {\n setArgument('server', zdyserver);\n if (getKey(String(cookie.getCookie(base_url)))) {\n let cookies = cookie.getCookie(base_url)\n try{cookie.removeCookie(base_url)} catch(e){cookie.removeCookie(base_url)}\n cookie.setCookie(zdyserver, cookies)\n }\n java.toast(`\\n\\n当前服务器为自定义服务器\\n${zdyserver}\\n\\n切换服务器请先清空服务器地址中的数据`);\n } else {\n zdyserver = '';\n }\n } catch (error) {\n zdyserver = '';\n }\n if (!zdyserver) {\n const servers = host\n const currentServer = getArgument('server') || '';\n const currentIndex = servers.indexOf(currentServer);\n\n const nextIndex = currentIndex >= 0 ? (currentIndex + 1) % servers.length : 0;\n const nextServer = servers[nextIndex];\n\n setArgument('server', nextServer);\n if (getKey(String(cookie.getCookie(currentServer)))) {\n let cookies = cookie.getCookie(currentServer)\ntry{cookie.removeCookie(currentServer)} catch(e){cookie.removeCookie(currentServer)}\n cookie.setCookie(nextServer, cookies)\n }\n java.longToast(`\\n\\n服务器【${nextIndex+1}】:${nextServer}`);\n }\n}\n\n//获取音色\nvar tone_id = getArgument('tone_id');\n\nfunction get_tone_id(arg) {\n var datadist = {\n \"0\": \"默认音色\",\n \"-1\": \"阅读模式\",\n \"-2\": \"漫画模式\",\n \"51\": \"多人发音\",\n \"1\": \"甜美少女\",\n \"2\": \"清亮青叔\",\n \"5\": \"开朗青年\",\n \"6\": \"温柔淑女\",\n \"4\": \"成熟大叔\",\n \"74\": \"大叔升级\",\n \"30\": \"优雅御姐\"\n };\n var tone_id = datadist[arg] || arg;\n var tishi = '\\n\\n当前音色' + tone_id;\n java.toast(tishi);\n}\n\n//设置音色\nfunction set_tone_id(mode, name) {\n putLoginInfo(JSON.stringify(result))\n let zdytone_id;\n try {\n zdytone_id = String(result['其他音色填写后点击右上角✔']);\n if (zdytone_id) {\n setArgument('tone_id', zdytone_id);\n java.toast(`\\n\\n当前音色为自定义音色\\n${zdytone_id}\\n\\n切换音色请先清空音色输入框中的数据`);\n } else {\n zdytone_id = '';\n }\n } catch (error) {\n zdytone_id = '';\n }\n if (!zdytone_id) {\n let toast = \"\\n\\n已切换至\" + name + '\\n\\n切换后需要刷新详情页';\n setArgument('tone_id', mode);\n java.toast(toast);\n }\n}\n\n//设置来源\nfunction set_source(sources) {\n let zdysources = String(result['自定义搜索源(多个用英文,分割)'] || '');\n if (zdysources.length > 1 && zdysources != 'undefined') {\n java.toast('\\n\\n请先清空自定义源再设置');\n } else {\n setArgument('source', sources);\n set_media('小说');\n java.toast(`\\n\\n当前来源已切换为:\\n${sources}\\n\\n切换后请重新搜索`);\n }\n}\n\n// 设置简介\nfunction set_info() {\n var info = getArgument('info');\n if (info == 'on') {\n setArgument('info', 'off');\n java.toast('\\n\\n已恢复详情页详细简介');\n } else {\n setArgument('info', 'on');\n java.toast('\\n\\n已精简详情页简介');\n }\n}\n\n// 番茄段评\nfunction paracomment() {\n var fqpara = getArgument('fqpara');\n if (fqpara == 'on') {\n setArgument('fqpara', 'off');\n java.longToast('\\n\\n段评已关闭');\n } else {\n setArgument('fqpara', 'on');\n java.longToast(\"\\n\\n段评已开启\\n\\n长按刷新段后面的图片即可\\n\\n如果图片不显示刷新无反应\\n请更新测试版阅读app\");\n }\n}\n\n\n// 我要推荐\nfunction put_book() {\n java.startBrowserAwait(getArgument('server') + '/put_book', '我来推荐');\n}\n\n// 书源更新\nfunction renderVersionPage() {\n let yd = '';\n let html = `\n<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>书源更新</title>\n <!-- Font Awesome 图标库 -->\n <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css\" />\n <style>\n :root {\n --primary-gradient: linear-gradient(135deg, #4e6ef2, #6b2dd8);\n --latest-gradient: linear-gradient(135deg, #8e2de2 0%, #4a00e0 50%, #d4af37 100%);\n --success-color: #28c76f;\n --warning-color: #ff9f43;\n --error-color: #ea5455;\n --text-main: #1f2937;\n --text-secondary: #6b7280;\n --card-bg: #ffffff;\n --border-color: #e5e7eb;\n --light-bg: #f9fafb;\n --shadow: 0 4px 12px rgba(78, 110, 242, 0.1);\n --shadow-hover: 0 6px 18px rgba(78, 110, 242, 0.2);\n --glow-shadow: 0 0 25px rgba(142, 45, 226, 0.5), 0 0 50px rgba(212, 175, 55, 0.3);\n --modal-bg: rgba(31, 41, 55, 0.8);\n --modal-content-bg: #ffffff;\n }\n\n * {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n font-family: 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif;\n }\n\n body {\n background: linear-gradient(135deg, #eef2ff, #f5f7ff);\n color: var(--text-main);\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 16px;\n }\n\n /* 加载动画 */\n .loading-wrapper {\n text-align: center;\n animation: fadeIn 0.3s ease;\n }\n\n .loading-spinner {\n width: 50px;\n height: 50px;\n border: 4px solid rgba(78, 110, 242, 0.3);\n border-top-color: #4e6ef2;\n border-radius: 50%;\n margin: 0 auto 20px;\n animation: spin 1s linear infinite;\n }\n\n .loading-text {\n color: var(--text-main);\n font-size: 16px;\n font-weight: 500;\n }\n\n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n\n @keyframes fadeIn {\n from { opacity: 0; transform: translateY(20px); }\n to { opacity: 1; transform: translateY(0); }\n }\n\n @keyframes slideIn {\n from { opacity: 0; transform: translateY(30px); }\n to { opacity: 1; transform: translateY(0); }\n }\n\n @keyframes pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.7; }\n }\n\n @keyframes gradientAnimation {\n 0% { background-position: 0% 50%; }\n 50% { background-position: 100% 50%; }\n 100% { background-position: 0% 50%; }\n }\n\n @keyframes breathe {\n 0%, 100% { \n transform: scale(1);\n box-shadow: var(--glow-shadow), var(--shadow);\n }\n 50% { \n transform: scale(1.02);\n box-shadow: 0 0 30px rgba(142, 45, 226, 0.6), 0 0 60px rgba(212, 175, 55, 0.4), var(--shadow);\n }\n }\n\n @keyframes shimmer {\n 0% {\n background-position: -200% center;\n }\n 100% {\n background-position: 200% center;\n }\n }\n\n /* 主容器 */\n .container {\n width: 100%;\n max-width: 420px;\n background: var(--card-bg);\n border-radius: 24px;\n overflow: hidden;\n box-shadow: var(--shadow);\n position: relative;\n z-index: 1;\n animation: slideIn 0.5s ease;\n display: none;\n }\n\n /* 头部 */\n .header {\n background: var(--primary-gradient);\n color: #ffffff;\n padding: 24px 16px;\n text-align: center;\n position: relative;\n overflow: hidden;\n }\n\n .header::before {\n content: '';\n position: absolute;\n top: -30px;\n left: -30px;\n width: 80px;\n height: 80px;\n background: rgba(255, 255, 255, 0.15);\n border-radius: 50%;\n }\n\n .header::after {\n content: '';\n position: absolute;\n bottom: -60px;\n right: -60px;\n width: 150px;\n height: 150px;\n background: rgba(255, 255, 255, 0.1);\n border-radius: 50%;\n }\n\n .header h1 {\n font-size: 1.4rem;\n font-weight: 700;\n margin-bottom: 8px;\n position: relative;\n z-index: 2;\n }\n\n .header p {\n font-size: 0.9rem;\n opacity: 0.9;\n line-height: 1.4;\n position: relative;\n z-index: 2;\n }\n\n .header-icon {\n font-size: 48px;\n margin-bottom: 10px;\n display: inline-block;\n animation: bounce 2s ease infinite;\n }\n\n @keyframes bounce {\n 0%, 100% { transform: translateY(0); }\n 50% { transform: translateY(-10px); }\n }\n\n /* 版本对比 */\n .version-comparison {\n display: flex;\n flex-wrap: nowrap;\n gap: 12px;\n padding: 16px;\n margin-top: 8px;\n position: relative;\n z-index: 10;\n }\n\n .version-card {\n flex: 1;\n min-width: 45%;\n background: var(--card-bg);\n border-radius: 16px;\n padding: 28px 16px 16px;\n box-shadow: var(--shadow);\n text-align: center;\n position: relative;\n transition: transform 0.3s ease, box-shadow 0.3s ease;\n overflow: hidden;\n border: 1px solid rgba(120, 130, 240, 0.1);\n }\n\n .version-card:hover {\n transform: translateY(-4px);\n box-shadow: var(--shadow-hover);\n }\n\n .version-card.current-version {\n background: linear-gradient(135deg, #ffffff 0%, #f8f9ff 100%);\n border: 1px solid rgba(78, 110, 242, 0.15);\n }\n\n .version-card.current-version:hover {\n box-shadow: 0 6px 20px rgba(78, 110, 242, 0.15);\n }\n\n .version-card.current-version h3,\n .version-card.current-version .version-number,\n .version-card.current-version .version-date {\n color: var(--text-main);\n }\n\n .version-card.latest-version {\n background: var(--latest-gradient);\n background-size: 300% 300%;\n box-shadow: var(--glow-shadow), var(--shadow);\n color: #fff;\n z-index: 2;\n animation: gradientAnimation 6s ease infinite, breathe 3s ease-in-out infinite;\n position: relative;\n overflow: hidden;\n }\n\n .version-card.latest-version::before {\n content: '';\n position: absolute;\n top: -50%;\n left: -50%;\n width: 200%;\n height: 200%;\n background: linear-gradient(\n 90deg,\n transparent,\n rgba(255, 255, 255, 0.3),\n transparent\n );\n transform: rotate(45deg);\n animation: shimmer 3s infinite;\n }\n\n .version-card.latest-version h3,\n .version-card.latest-version .version-number,\n .version-card.latest-version .version-date {\n color: #fff;\n text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n position: relative;\n z-index: 1;\n }\n\n .version-status {\n position: absolute;\n top: 6px;\n right: 6px;\n padding: 3px 7px;\n font-size: 0.65rem;\n font-weight: 600;\n border-radius: 6px;\n color: #fff;\n line-height: 1.2;\n white-space: nowrap;\n z-index: 2;\n }\n\n .version-card.latest-version .version-status {\n background: rgba(255, 255, 255, 0.25);\n backdrop-filter: blur(5px);\n border: 1px solid rgba(255, 255, 255, 0.3);\n color: #fff;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);\n }\n\n .status-outdated { background: var(--warning-color); }\n .status-latest { background: var(--success-color); }\n .status-invalid { background: var(--error-color); }\n\n .version-card h3 {\n font-size: 0.9rem;\n color: var(--text-secondary);\n margin-bottom: 8px;\n font-weight: 500;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n }\n\n .version-number {\n font-size: 1.25rem;\n font-weight: 700;\n color: var(--text-main);\n margin: 8px 0;\n transition: all 0.3s ease;\n font-family: 'Courier New', monospace;\n }\n\n .version-card.latest-version .version-number {\n font-size: 1.4rem;\n transform: scale(1.05);\n text-shadow: \n 0 2px 4px rgba(0, 0, 0, 0.3),\n 0 0 10px rgba(212, 175, 55, 0.8),\n 0 0 20px rgba(212, 175, 55, 0.5);\n animation: pulse-glow 2s ease-in-out infinite;\n }\n\n @keyframes pulse-glow {\n 0%, 100% {\n text-shadow: \n 0 2px 4px rgba(0, 0, 0, 0.3),\n 0 0 10px rgba(212, 175, 55, 0.8),\n 0 0 20px rgba(212, 175, 55, 0.5);\n }\n 50% {\n text-shadow: \n 0 2px 4px rgba(0, 0, 0, 0.3),\n 0 0 15px rgba(212, 175, 55, 1),\n 0 0 30px rgba(212, 175, 55, 0.7);\n }\n }\n\n .version-date {\n font-size: 0.8rem;\n color: var(--text-secondary);\n margin-top: 4px;\n }\n\n /* 版本对比指示器 */\n .version-indicator {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n z-index: 5;\n width: 32px;\n height: 32px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);\n color: white;\n }\n\n .version-indicator.update-needed {\n background: var(--error-color);\n box-shadow: 0 2px 8px rgba(234, 84, 85, 0.4);\n animation: pulse-indicator 1.5s infinite;\n }\n\n .version-indicator.is-latest {\n background: var(--success-color);\n box-shadow: 0 2px 8px rgba(40, 199, 111, 0.4);\n }\n\n @keyframes pulse-indicator {\n 0% { transform: translate(-50%, -50%) scale(1); }\n 50% { transform: translate(-50%, -50%) scale(1.1); }\n 100% { transform: translate(-50%, -50%) scale(1); }\n }\n\n /* 内容区 */\n .content-container {\n padding: 16px;\n }\n\n /* 状态提示 */\n .status-alert {\n background: var(--card-bg);\n border-radius: 16px;\n box-shadow: var(--shadow);\n margin-bottom: 16px;\n overflow: hidden;\n border: 1px solid rgba(120, 130, 240, 0.1);\n padding: 12px 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 10px;\n font-weight: 500;\n font-size: 14px;\n animation: slideIn 0.5s ease 0.3s backwards;\n }\n\n .status-alert i {\n font-size: 20px;\n }\n\n .status-alert.update-available {\n background: linear-gradient(135deg, #ffeaa7 0%, #fdcb6e 100%);\n color: #d63031;\n box-shadow: 0 4px 15px rgba(253, 203, 110, 0.4);\n }\n\n .status-alert.up-to-date {\n background: linear-gradient(135deg, #55efc4 0%, #00b894 100%);\n color: white;\n box-shadow: 0 4px 15px rgba(0, 184, 148, 0.4);\n }\n\n /* 更新容器 */\n .update-container {\n background: var(--card-bg);\n border-radius: 16px;\n box-shadow: var(--shadow);\n margin-bottom: 16px;\n overflow: hidden;\n border: 1px solid rgba(120, 130, 240, 0.1);\n animation: slideIn 0.5s ease 0.4s backwards;\n }\n\n .update-header {\n background: var(--light-bg);\n padding: 12px 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n border-bottom: 1px solid var(--border-color);\n }\n\n .update-header h2 {\n font-size: 1rem;\n font-weight: 600;\n color: var(--text-main);\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .update-header h2 i {\n color: #4e6ef2;\n }\n\n .update-tag {\n background: rgba(78, 110, 242, 0.1);\n color: #4e6ef2;\n padding: 4px 8px;\n border-radius: 8px;\n font-size: 0.75rem;\n font-weight: 600;\n }\n\n .update-content {\n padding: 16px;\n }\n\n .update-date {\n font-weight: 600;\n color: #4e6ef2;\n margin-bottom: 12px;\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 0;\n border-bottom: 1px dashed #e0e0e0;\n }\n\n .update-text {\n margin: 8px 0;\n position: relative;\n padding-left: 16px;\n line-height: 1.5;\n color: var(--text-main);\n font-size: 0.95rem;\n white-space: pre-wrap;\n word-break: break-word;\n }\n\n .update-text::before {\n content: '•';\n position: absolute;\n left: 0;\n font-weight: bold;\n color: #4e6ef2;\n font-size: 1.2rem;\n line-height: 1;\n }\n\n /* 历史日志 */\n .history-container {\n background: var(--card-bg);\n border-radius: 16px;\n box-shadow: var(--shadow);\n margin-bottom: 16px;\n border: 1px solid rgba(120, 130, 240, 0.1);\n animation: slideIn 0.5s ease 0.5s backwards;\n }\n\n .history-header {\n background: var(--light-bg);\n padding: 12px 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n border-bottom: 1px solid var(--border-color);\n cursor: pointer;\n user-select: none;\n }\n\n .history-header:hover {\n opacity: 0.8;\n }\n\n .history-header h2 {\n font-size: 1rem;\n font-weight: 600;\n color: var(--text-main);\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .history-header h2 i {\n color: #4e6ef2;\n }\n\n .toggle-history {\n background: none;\n border: none;\n color: var(--text-secondary);\n cursor: pointer;\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 0.85rem;\n transition: color 0.2s ease;\n }\n\n .toggle-history:hover {\n color: #4e6ef2;\n }\n\n .history-content {\n padding: 0 16px;\n max-height: 0;\n overflow: hidden;\n transition: max-height 0.4s ease, padding 0.4s ease;\n }\n\n .history-content.expanded {\n max-height: 60vh;\n overflow-y: auto;\n padding: 16px;\n scrollbar-width: thin;\n scrollbar-color: #4e6ef2 #f0f0f0;\n }\n\n .history-content.expanded::-webkit-scrollbar {\n width: 6px;\n }\n\n .history-content.expanded::-webkit-scrollbar-track {\n background: #f0f0f0;\n border-radius: 4px;\n }\n\n .history-content.expanded::-webkit-scrollbar-thumb {\n background: #4e6ef2;\n border-radius: 4px;\n }\n\n .history-content.expanded::-webkit-scrollbar-thumb:hover {\n background: #3a56d0;\n }\n\n .history-item {\n margin-bottom: 16px;\n padding-bottom: 16px;\n border-bottom: 1px dashed var(--border-color);\n }\n\n .history-item:last-child {\n border-bottom: none;\n margin-bottom: 0;\n padding-bottom: 0;\n }\n\n .history-date {\n font-weight: 600;\n color: var(--text-main);\n margin-bottom: 8px;\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 0.9rem;\n background: rgba(78, 110, 242, 0.05);\n padding: 6px 10px;\n border-radius: 6px;\n }\n\n .history-text {\n margin: 8px 0;\n padding-left: 16px;\n line-height: 1.4;\n color: var(--text-secondary);\n position: relative;\n font-size: 0.9rem;\n white-space: pre-wrap;\n word-break: break-word;\n }\n\n .history-text::before {\n content: '•';\n position: absolute;\n left: 0;\n color: #4e6ef2;\n font-weight: bold;\n font-size: 1.2rem;\n line-height: 1;\n }\n\n /* 按钮组 */\n .button-group {\n display: flex;\n flex-direction: column;\n gap: 10px;\n margin-bottom: 16px;\n }\n\n .button {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 10px;\n padding: 14px 28px;\n text-align: center;\n font-size: 1rem;\n border: none;\n border-radius: 12px;\n text-decoration: none;\n background: var(--primary-gradient);\n color: white;\n font-weight: 600;\n transition: all 0.3s ease;\n box-shadow: var(--shadow);\n position: relative;\n overflow: hidden;\n cursor: pointer;\n }\n\n .button i {\n font-size: 1rem;\n }\n\n .button::after {\n content: '';\n position: absolute;\n top: -50%;\n left: -50%;\n width: 200%;\n height: 200%;\n background: rgba(255, 255, 255, 0.1);\n transform: rotate(30deg);\n transition: all 0.6s ease;\n pointer-events: none;\n }\n\n .button:hover {\n transform: translateY(-3px);\n box-shadow: var(--shadow-hover);\n }\n\n .button:hover::after {\n transform: rotate(30deg) translate(20%, 20%);\n }\n\n .button:active {\n transform: scale(0.95);\n }\n\n /* 错误状态 */\n .error-state {\n text-align: center;\n padding: 40px 20px;\n color: var(--text-main);\n }\n\n .error-icon {\n font-size: 64px;\n margin-bottom: 20px;\n color: var(--error-color);\n }\n\n .error-text {\n font-size: 16px;\n line-height: 1.6;\n margin-bottom: 20px;\n }\n\n .retry-button {\n background: var(--primary-gradient);\n color: white;\n padding: 12px 30px;\n border-radius: 12px;\n border: none;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.3s ease;\n font-size: 14px;\n box-shadow: var(--shadow);\n }\n\n .retry-button:hover {\n transform: translateY(-2px);\n box-shadow: var(--shadow-hover);\n }\n\n .retry-button:active {\n transform: scale(0.95);\n }\n\n /* 装饰元素 */\n .decoration {\n position: absolute;\n z-index: 0;\n pointer-events: none;\n }\n\n .decoration.circle {\n width: 120px;\n height: 120px;\n border-radius: 50%;\n background: rgba(107, 45, 216, 0.05);\n top: 10%;\n left: 10%;\n }\n\n .decoration.square {\n width: 80px;\n height: 80px;\n transform: rotate(45deg);\n background: rgba(78, 110, 242, 0.05);\n bottom: 10%;\n right: 10%;\n }\n\n /* 响应式 */\n @media (max-width: 768px) {\n body {\n padding: 12px;\n }\n\n .container {\n max-width: 100%;\n border-radius: 20px;\n }\n\n .header {\n padding: 20px 15px;\n }\n\n .header h1 {\n font-size: 1.3rem;\n }\n\n .header-icon {\n font-size: 40px;\n }\n\n .version-comparison {\n flex-direction: row;\n flex-wrap: nowrap;\n gap: 10px;\n padding: 12px;\n margin-top: 6px;\n overflow-x: auto;\n }\n\n .version-card {\n min-width: 45%;\n padding: 26px 12px 12px;\n }\n\n /* 移动端减弱呼吸动效 */\n .version-card.latest-version {\n animation: gradientAnimation 6s ease infinite;\n }\n\n .version-status {\n top: 5px;\n right: 5px;\n padding: 2px 5px;\n font-size: 0.6rem;\n }\n\n .version-number {\n font-size: 1.1rem;\n }\n\n .version-card.latest-version .version-number {\n font-size: 1.2rem;\n }\n\n .update-header h2, .history-header h2 {\n font-size: 0.9rem;\n }\n\n .button {\n padding: 12px 24px;\n font-size: 0.95rem;\n }\n\n .history-content.expanded {\n max-height: 50vh;\n -webkit-overflow-scrolling: touch;\n }\n }\n\n @media (max-width: 380px) {\n .header h1 {\n font-size: 1.2rem;\n }\n\n .version-number {\n font-size: 1rem;\n }\n\n .version-card.latest-version .version-number {\n font-size: 1.1rem;\n }\n\n .button {\n padding: 11px;\n font-size: 0.9rem;\n }\n }\n </style>\n</head>\n<body>\n <div class=\"decoration circle\"></div>\n <div class=\"decoration square\"></div>\n\n <div id=\"loading\" class=\"loading-wrapper\">\n <div class=\"loading-spinner\"></div>\n <div class=\"loading-text\"><i class=\"fas fa-search\"></i> 正在检查更新...</div>\n </div>\n\n <div class=\"container\" id=\"container\">\n <div class=\"header\">\n <div class=\"header-icon\"><i class=\"fas fa-book\"></i></div>\n <h1>大灰狼书源更新</h1>\n <p>请使用阅读测试版,不支持正式版</p>\n </div>\n\n <div class=\"version-comparison\">\n <div class=\"version-card current-version\">\n <div class=\"version-status status-outdated\" id=\"currentStatus\">待检查</div>\n <h3><i class=\"fas fa-cube\"></i> 当前版本</h3>\n <div class=\"version-number\" id=\"currentVersion\">-</div>\n <div class=\"version-date\">您的当前版本</div>\n </div>\n\n <div class=\"version-indicator update-needed\" id=\"versionIndicator\" style=\"display: none;\">\n <i class=\"fas fa-arrow-right\"></i>\n </div>\n\n <div class=\"version-card latest-version\">\n <div class=\"version-status status-latest\" id=\"latestStatus\">最新版本</div>\n <h3><i class=\"fas fa-star\"></i> 最新版本</h3>\n <div class=\"version-number\" id=\"latestVersion\">-</div>\n <div class=\"version-date\">可用最新版本</div>\n </div>\n </div>\n\n <div class=\"content-container\">\n <div class=\"status-alert\" id=\"statusAlert\" style=\"display: none;\"></div>\n\n <div id=\"latestLogContainer\" style=\"display: none;\">\n <div class=\"update-container\">\n <div class=\"update-header\">\n <h2><i class=\"fas fa-bolt\"></i> 最新更新</h2>\n <div class=\"update-tag\">最新发布</div>\n </div>\n <div class=\"update-content\">\n <div class=\"update-date\" id=\"latestLogDate\"></div>\n <div class=\"update-text\" id=\"latestLogContent\"></div>\n </div>\n </div>\n </div>\n\n <div class=\"button-group\" id=\"buttonGroup\" style=\"display: none;\"></div>\n\n <div class=\"history-container\" id=\"logs\" style=\"display: none;\">\n <div class=\"history-header\" onclick=\"toggleLogs()\">\n <h2><i class=\"fas fa-history\"></i> 历史更新 <span id=\"historyCount\"></span></h2>\n <button class=\"toggle-history\" id=\"toggleButton\">\n <span id=\"toggleText\">展开历史</span>\n <i class=\"fas fa-chevron-down\" id=\"toggleIcon\"></i>\n </button>\n </div>\n <div class=\"history-content\" id=\"logList\"></div>\n </div>\n </div>\n </div>\n\n <script>\n let logsCollapsed = true;\n\n function toggleLogs() {\n logsCollapsed = !logsCollapsed;\n const logList = document.getElementById('logList');\n const toggleText = document.getElementById('toggleText');\n const toggleIcon = document.getElementById('toggleIcon');\n \n if (logsCollapsed) {\n logList.classList.remove('expanded');\n toggleText.textContent = '展开历史';\n toggleIcon.className = 'fas fa-chevron-down';\n } else {\n logList.classList.add('expanded');\n toggleText.textContent = '收起历史';\n toggleIcon.className = 'fas fa-chevron-up';\n }\n }\n\n (async function() {\n const loading = document.getElementById('loading');\n const container = document.getElementById('container');\n const currentVersion = document.getElementById('currentVersion');\n const latestVersion = document.getElementById('latestVersion');\n const currentStatus = document.getElementById('currentStatus');\n const latestStatus = document.getElementById('latestStatus');\n const versionIndicator = document.getElementById('versionIndicator');\n const statusAlert = document.getElementById('statusAlert');\n const buttonGroup = document.getElementById('buttonGroup');\n const latestLogContainer = document.getElementById('latestLogContainer');\n const latestLogDate = document.getElementById('latestLogDate');\n const latestLogContent = document.getElementById('latestLogContent');\n const logsContainer = document.getElementById('logs');\n const logList = document.getElementById('logList');\n const historyCount = document.getElementById('historyCount');\n\n const localVer = '${String(localVersion)}';\n\n // 统一的服务器配置 - 方便维护\n const serverConfig = {\n main: {\n name: '主线路',\n icon: 'rocket',\n baseUrl: 'https://sy.dashabi.tk',\n downloadPath: '/download/%E5%AE%89%E5%8D%93%E9%98%85%E8%AF%BBapp-%E5%A4%A7%E7%81%B0%E7%8B%BC%E8%9E%8D%E5%90%884.0(%E5%AE%8C%E5%85%A8%E7%89%88).json'\n },\n backup1: {\n name: '备用线路1',\n icon: 'box',\n baseUrl: 'https://api.langge.cf',\n downloadPath: '/sy/download/%E5%AE%89%E5%8D%93%E9%98%85%E8%AF%BBapp-%E5%A4%A7%E7%81%B0%E7%8B%BC%E8%9E%8D%E5%90%884.0(%E5%AE%8C%E5%85%A8%E7%89%88).json'\n },\n backup2: {\n name: '备用线路2',\n icon: 'satellite',\n baseUrl: 'https://20.langge.tk',\n downloadPath: '/sy/download/%E5%AE%89%E5%8D%93%E9%98%85%E8%AF%BBapp-%E5%A4%A7%E7%81%B0%E7%8B%BC%E8%9E%8D%E5%90%884.0(%E5%AE%8C%E5%85%A8%E7%89%88).json'\n },\n backup3: {\n name: '备用线路3',\n icon: 'link',\n baseUrl: 'http://219.154.201.122:5006',\n downloadPath: '/sy/download/%E5%AE%89%E5%8D%93%E9%98%85%E8%AF%BBapp-%E5%A4%A7%E7%81%B0%E7%8B%BC%E8%9E%8D%E5%90%884.0(%E5%AE%8C%E5%85%A8%E7%89%88).json'\n },\n backup4: {\n name: '备用线路4',\n icon: 'bolt',\n baseUrl: 'https://v2.czyl.cf',\n downloadPath: '/sy/download/%E5%AE%89%E5%8D%93%E9%98%85%E8%AF%BBapp-%E5%A4%A7%E7%81%B0%E7%8B%BC%E8%9E%8D%E5%90%884.0(%E5%AE%8C%E5%85%A8%E7%89%88).json'\n },\n backup5: {\n name: '备用线路5',\n icon: 'globe',\n baseUrl: 'https://v7.czyl.cf',\n downloadPath: '/sy/download/%E5%AE%89%E5%8D%93%E9%98%85%E8%AF%BBapp-%E5%A4%A7%E7%81%B0%E7%8B%BC%E8%9E%8D%E5%90%884.0(%E5%AE%8C%E5%85%A8%E7%89%88).json'\n },\n backup6: {\n name: '备用线路6',\n icon: 'broadcast-tower',\n baseUrl: 'https://v8.czyl.cf',\n downloadPath: '/sy/download/%E5%AE%89%E5%8D%93%E9%98%85%E8%AF%BBapp-%E5%A4%A7%E7%81%B0%E7%8B%BC%E8%9E%8D%E5%90%884.0(%E5%AE%8C%E5%85%A8%E7%89%88).json'\n }\n };\n\n // 版本比较函数\n function compareVersions(vs) {\n const normalize = (v) => {\n return v.split('.').map(n => {\n const num = parseInt(n, 10);\n return isNaN(num) ? 0 : num;\n });\n };\n\n const parts1 = normalize(localVer);\n const parts2 = normalize(vs);\n const maxLength = Math.max(parts1.length, parts2.length);\n \n for (let i = 0; i < maxLength; i++) {\n const num1 = parts1[i] || 0;\n const num2 = parts2[i] || 0;\n if (num1 > num2) return 1;\n if (num1 < num2) return -1;\n }\n return 0;\n }\n\n async function fetchVersionData() {\n // 使用统一配置中除主线路外的备用线路进行版本检查\n const serversToCheck = Object.values(serverConfig).filter(s => s.baseUrl.includes('czyl.cf') || s.baseUrl.includes('langge.tk') || s.baseUrl.includes('langge.cf') || s.baseUrl.includes('219.154'));\n \n for (const server of serversToCheck) {\n try {\n const response = await fetch(server.baseUrl + '/version', { timeout: 2000 });\n if (response.ok) {\n return await response.json();\n }\n } catch (e) {\n console.warn(\\`接口失败:\\${server.baseUrl}\\`, e);\n }\n }\n throw new Error('所有更新接口都请求失败');\n }\n\n function showError(message) {\n loading.innerHTML = \\`\n <div class=\"error-state\">\n <div class=\"error-icon\"><i class=\"fas fa-exclamation-triangle\"></i></div>\n <div class=\"error-text\">\\${message}</div>\n <button class=\"retry-button\" onclick=\"location.reload()\"><i class=\"fas fa-redo\"></i> 重试</button>\n </div>\n \\`;\n }\n\n try {\n const data = await fetchVersionData();\n const cloudVersion = String(data.version3);\n const updateLog = data.update_log || {};\n\n // 显示版本信息\n currentVersion.textContent = \\`v\\${localVer}\\`;\n latestVersion.textContent = \\`v\\${cloudVersion}\\`;\n\n // 处理日志\n const logEntries = Object.entries(updateLog);\n if (logEntries.length > 0) {\n // 显示最新日志\n const [latestDate, latestContent] = logEntries[0];\n latestLogDate.innerHTML = \\`<i class=\"fas fa-calendar-alt\"></i> \\${latestDate}\\`;\n latestLogContent.textContent = latestContent;\n latestLogContainer.style.display = 'block';\n\n // 显示历史日志\n if (logEntries.length > 1) {\n const historyLogs = logEntries.slice(1);\n historyCount.textContent = \\`(\\${historyLogs.length}条)\\`;\n logList.innerHTML = historyLogs.map(([date, content]) => \\`\n <div class=\"history-item\">\n <div class=\"history-date\">\n <i class=\"fas fa-calendar-day\"></i>\n <span>\\${date}</span>\n </div>\n <div class=\"history-text\">\\${content}</div>\n </div>\n \\`).join('');\n logsContainer.style.display = 'block';\n }\n }\n\n // 检查更新状态\n const compareResult = compareVersions(cloudVersion);\n \n // 显示版本指示器\n versionIndicator.style.display = 'flex';\n \n if (compareResult === -1) {\n // 需要更新\n currentStatus.textContent = '待更新';\n currentStatus.className = 'version-status status-outdated';\n versionIndicator.className = 'version-indicator update-needed';\n versionIndicator.innerHTML = '<i class=\"fas fa-arrow-right\"></i>';\n\n // 使用统一配置生成下载按钮\n buttonGroup.innerHTML = Object.values(serverConfig).map(server => {\n const fullUrl = server.baseUrl + server.downloadPath;\n return \\`\n <a href=\"yuedu://booksource/importonline?src=\\${encodeURIComponent(fullUrl)}\" class=\"button\">\n <i class=\"fas fa-\\${server.icon}\"></i>\n <span>\\${server.name}</span>\n </a>\n \\`;\n }).join('');\n buttonGroup.style.display = 'flex';\n } else {\n // 已是最新版本\n currentStatus.textContent = '最新';\n currentStatus.className = 'version-status status-latest';\n versionIndicator.className = 'version-indicator is-latest';\n versionIndicator.innerHTML = '<i class=\"fas fa-check\"></i>';\n \n statusAlert.className = 'status-alert up-to-date';\n statusAlert.innerHTML = '<i class=\"fas fa-check-circle\"></i> <div>您已是最新版本</div>';\n statusAlert.style.display = 'flex';\n }\n\n // 显示主容器,隐藏加载\n loading.style.display = 'none';\n container.style.display = 'block';\n\n } catch (err) {\n console.error('版本检查失败:', err);\n showError('<i class=\"fas fa-exclamation-circle\"></i> 检查更新失败,请稍后重试<br><small>' + err.message + '</small>');\n }\n })();\n </script>\n</body>\n</html>\n`;\n java.startBrowser(`data:text/html;base64,${java.base64Encode(html)}`, '大灰狼书源更新');\n}",
"respondTime": 180000,
"ruleBookInfo": {
"author": "$.author",
"canReName": "1",
"coverUrl": "$.thumb_url",
"init": "<js>\nif (String(baseUrl).startsWith(\"data:\")) {\n let res = JSON.parse(java.hexDecodeToString(result));\n let book_id = res.book_id;\n let tab = res.tab;\n let sources = res.sources;\n let url = res.url;\n let html = \"\";\n let proxy = getArguments(source.getVariable(), \"proxy\");\n if (url != \"\" && proxy == \"本地\") {\n if (sources == '69书吧') {\n let ck69 = String(cookie.getCookie(url));\n let headers = {\n \"Cookie\": ck69\n };\n let op = JSON.stringify({\n \"headers\": headers\n });\n html = java.ajax(url + ',' + op);\n } else {\n html = java.ajax(url);\n }\n //java.log(html);\n if (html.includes(\"Just a moment...\") && sources == '69书吧') {\n cookie.removeCookie(url);\n var x = `https://www.69shuba.com`;\n java.longToast('需要真人验证,请进入任意书籍详情页过验证');\n var s = java.startBrowserAwait(x, \"需要真人验证,请进入任意书籍详情页过验证\").body();\n\n let ck69 = String(cookie.getCookie(url));\n let headers = {\n \"Cookie\": ck69\n };\n let op = JSON.stringify({\n \"headers\": headers\n });\n java.log(op);\n html = java.ajax(url + ',' + op);\n //java.log(html);\n }\n }\n let base_url = getArguments(source.getVariable(), \"server\");\n let op = {\n method: \"POST\",\n body: {\n html: html\n }\n };\n op = JSON.stringify(op);\n let varia = String(book.getVariable('custom'));\n if (varia == 'null') {\n varia = '';\n }\n varia = JSON.stringify({\n 'custom': varia\n });\n //varia = java.base64Encode(varia);\n java.log(`${base_url}/detail?book_id=${book_id}&source=${sources}&tab=${tab}&variable=${varia},${op}`);\n result = java.ajax(`${base_url}/detail?book_id=${book_id}&source=${sources}&tab=${tab}&variable=${varia},${op}`);\n}\nresult\n</js>$.data",
"intro": "<js>\nlet {\n book_id,\n source: sources,\n tab,\n book_tts,\n tags,\n role,\n last_chapter_title,\n last_chapter_update_time,\n word_number,\n status,\n score,\n abstract,\n copyright_info\n} = result;\nlet proxy = getArguments(source.getVariable(), \"proxy\");\nif (proxy == \"本地\") {\n proxy = \"本地网络\";\n} else {\n proxy = \"服务器网络\";\n}\njava.put(\"book_detail\", JSON.stringify(result));\n\nlet base_url = getArguments(source.getVariable(), \"server\");\nlet key = \"\";\ntry {\n let cookieValue =\n String(cookie.getCookie(base_url)) || String(java.getCookie(base_url));\n key = getKey(cookieValue);\n} catch (e) {\n key = \"\";\n}\n\nif (key == \"\") {\n java.log(\"当前服务器未查询到登录状态,尝试查询其他服务器登录状态...\");\n let cookieValue;\n for (let h of host) {\n try {\n cookieValue = String(cookie.getCookie(h)) || String(java.getCookie(h));\n key = getKey(cookieValue);\n } catch (e) {\n key = \"\";\n }\n if (key) {\n java.log(`已在${h}登录,退出查询,正在转移登录状态到当前服务${base_url}`);\n //java.log(cookieValue)\n cookie.removeCookie(h);\n cookie.removeCookie(base_url);\n cookie.setCookie(base_url, cookieValue);\n break;\n }\n }\n}\n\nif (book.readConfig == null || book.readConfig.useReplaceRule == null) {\n book.setUseReplaceRule(false);\n}\n\nlet nickname = '账号状态:⚠️ 未登录 | 点击右上角 🔖 登录';\ntry {\n let opcx = {\n method: \"GET\",\n headers: {\n cookie: 'qttoken=' + key + ';'\n },\n };\n opcx = JSON.stringify(opcx);\n let user_info = JSON.parse(java.ajax(base_url + '/get_avatar,' + opcx));\n if (user_info.code == 0) {\n if (user_info.nickname) {\n nickname = '欢迎回来:' + user_info.nickname\n } else {\n nickname = '欢迎回来:' + user_info.email + \"(请前往用户后台设置用户名)\"\n }\n }\n} catch (e) {\n if (key) {\n nickname = '账号状态:已登录'\n }\n};\n\nlet loginStatus = nickname;\n\nlet lightDivider = \"❇️───────❇️───────❇️\";\nlet heavyDivider = \"&lrm;\\n&lrm;\";\n\nlet isValid = (value) => String(value).length > 1;\n\nlet info = `\n 📡 当前服务:${base_url}\n 🔑 ${loginStatus}\n 🏷 数据来源:${sources}\n 🔄 当前模式:${tab}\n ⚙️ 访问模式:${proxy}\n`;\n\nif (tab == \"听书\") {\n let toneId = getArguments(source.getVariable(), \"tone_id\");\n if (isValid(book_tts)) {\n info += `${lightDivider}\n 🎵 音色配置:${toneId}\n ${book_tts}\n`;\n }\n}\n\nlet basicInfo = \"\";\nlet addBasicInfo = (value, prefix, icon) => {\n if (isValid(value)) basicInfo += ` ${icon} ${prefix} ${value}\\n`;\n};\n\naddBasicInfo(tags, \"书籍分类:\", \"🌈\");\naddBasicInfo(role, \"书籍主角:\", \"👑\");\naddBasicInfo(last_chapter_title, \"最新章节:\", \"📚\");\naddBasicInfo(last_chapter_update_time, \"更新时间:\", \"⏳\");\naddBasicInfo(word_number, \"书籍字数:\", \"📊\");\naddBasicInfo(status, \"书籍状态:\", \"🚩\");\naddBasicInfo(score, \"书籍评分:\", \"⭐\");\n\nif (basicInfo) info += `${lightDivider}\\n${basicInfo}`;\n\nif (isValid(abstract)) {\n let indentedAbstract = abstract\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n info += `${heavyDivider}\n 📖 书籍简介:\n${indentedAbstract}\n`;\n} else {\n info += `${heavyDivider}`;\n}\n\nif (isValid(copyright_info)) {\n info += `${lightDivider}\n © ${copyright_info}\n`;\n} else {\n info += `${lightDivider}`;\n}\n\ninfo += `\n${heavyDivider}\n 💠💠💠 数据更新于 ${new Date().toLocaleString()} 💠💠💠\n`;\nlet jjinfo = getArguments(source.getVariable(), \"info\");\n\nif (jjinfo != \"on\") {\n info = String(info)\n .split(\"\\n\")\n .map((line) => line.replace(/^ {4}/, \"\"))\n .join(\"\\n\");\n} else {\n basicInfo = \"\";\n addBasicInfo(last_chapter_title, \"最新章节:\", \"📚\");\n addBasicInfo(last_chapter_update_time, \"更新时间:\", \"⏳\");\n addBasicInfo(word_number, \"书籍字数:\", \"📊\");\n addBasicInfo(status, \"书籍状态:\", \"🚩\");\n addBasicInfo(score, \"书籍评分:\", \"⭐\");\n if (isValid(abstract)) {\n let indentedAbstract = abstract\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n basicInfo += `\n \\n&lrm;\n 📖 书籍简介:\n${indentedAbstract}\n`;\n } else {\n basicInfo += `${heavyDivider}`;\n }\n info = `&lrm;\\n🏷 数据来源:${sources}\\n` + basicInfo;\n}\n</js>",
"lastChapter": "{{$.source}} {{$.last_chapter_title}} {{$.last_chapter_update_time}}",
"name": "$.book_name",
"tocUrl": "<js>\nlet book_id = result.book_id;\nlet sources = result.source;\nlet tab = result.tab || \"小说\";\nlet url = result.toc_url || \"\";\nvar sdtoken;\ntry {\n var loginInfoMap = source.getLoginInfoMap ? source.getLoginInfoMap() : {};\n sdtoken = String(loginInfoMap['手动填写番茄token(可不填)'] || '');\n} catch (e) {\n sdtoken = '';\n}\nvar rawCookie = getFanqieCookie() || sdtoken;\nvar match = rawCookie.match(/sessionid=[^;]+/);\nvar fqcookie = match ? match[0] : '';\nvar fqssionid = '';\nif (fqcookie) {\n fqssionid = getSessionId(fqcookie);\n};\nsetArguments('fqssionid', fqssionid);\njava.put(\"tab\", tab);\njava.put(\"book_id\", book_id);\nlet qtcatalog = {\n book_id: book_id,\n sources: sources,\n tab: tab,\n url: url,\n};\nqtcatalog = java.base64Encode(JSON.stringify(qtcatalog));\n`data:;base64,${qtcatalog},{\"type\":\"qingtian2\"}`;\n</js>",
"wordCount": "$.word_number"
},
"ruleContent": {
"content": "<js>\nresult = String(java.hexDecodeToString(result));\nlet res;\nif (result.match(/大灰狼融合/)) {\n result = result.split(\"大灰狼融合4\");\n res = {\n item_id: result[0],\n tab: result[1],\n title: result[2],\n sources: result[3],\n url: \"\"\n };\n} else {\n res = JSON.parse(result);\n}\nlet varia = String(book.getVariable('custom'));\nif (varia == 'null') {\n varia = '';\n}\nvaria = JSON.stringify({\n 'custom': varia\n});\n// varia = java.base64Encode(varia);\nlet book_id = res.book_id;\nlet item_id = res.item_id;\nlet tab = res.tab;\nlet title = res.title;\nlet sources = res.sources;\nlet url = res.url;\nlet html = \"\";\nlet proxy = getArguments(source.getVariable(), \"proxy\");\nif (url != \"\" && proxy == \"本地\") {\n if (sources == '69书吧') {\n let ck69 = String(cookie.getCookie(url));\n let headers = {\n \"Cookie\": ck69,\n \"User-Agent\": \"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Mobile Safari/537.36 Edg/138.0.0.0\"\n };\n let op = JSON.stringify({\n \"headers\": headers\n });\n java.log(url);\n java.log(op);\n html = java.ajax(url + ',' + op);\n } else {\n html = java.ajax(url);\n }\n java.log(html);\n if (html.includes(\"Just a moment...\") && sources == '69书吧' && book.durChapterIndex === chapter.index) {\n cookie.removeCookie(url);\n var x = url;\n java.longToast('需要真人验证,请进入任意书籍详情页过验证');\n var s = java.startBrowserAwait(x, \"需要真人验证,请进入任意书籍详情页过验证\").body();\n\n let ck69 = String(cookie.getCookie(url));\n let headers = {\n \"User-Agent\": \"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Mobile Safari/537.36 Edg/138.0.0.0\",\n \"Cookie\": ck69\n };\n let op = JSON.stringify({\n \"headers\": headers\n });\n java.log(op);\n html = java.ajax(url + ',' + op);\n //java.log(html);\n }\n};\nlet content = \"\";\nlet data;\nlet tone_id = getArguments(source.getVariable(), \"tone_id\");\nlet base_url = getArguments(source.getVariable(), \"server\");\nlet device, device_type;\ntry {\n device = java.deviceID();\n device_type = \"苹果\";\n} catch (e) {\n try {\n device = java.androidId();\n device_type = \"安卓\";\n } catch (e) {\n device = \"\";\n device_type = \"安卓\";\n }\n}\n\nlet qtcookie = cookie.getCookie(base_url);\ntry {\n qtcookie = `qttoken=${String(cookie.getKey(base_url, \"qttoken\"))}; deviceId=${device};`\n} catch (e) {}\n\nvar params = {\n html: html,\n item_id: item_id,\n source: sources,\n tab: tab,\n tone_id: tone_id,\n variable: varia,\n version: '4.11.5.1'\n};\n\nvar content_url = '/content';\n\nif ((sources == \"番茄\" || sources == \"七猫\" || sources == \"塔读\") && getArguments(source.getVariable(), \"fqpara\") == \"on\" && tab == \"小说\") {\n content_url = '/content?review=1';\n}\n\n//var signInfo = generateComplexSignature('POST', content_url, {}, APP_SECRET, params);\n\n\n//var signedParams = copyObject(params);\n//signedParams.timestamp = signInfo.timestamp;\n//signedParams.nonce = signInfo.nonce;\n//signedParams.sign = signInfo.sign;\n\n\nvar op = {\n method: \"POST\",\n body: JSON.stringify(params),\n headers: {\n cookie: qtcookie,\n 'Content-Type': 'application/json'\n }\n};\n\nop = JSON.stringify(op);\njava.log(op);\ndata = java.ajax(base_url + content_url + `,${op}`);\n\ntry {\n data = JSON.parse(data);\n if (data.msg) {\n java.toast(data.msg);\n }\n} catch (e) {}\n\ncontent = data.content\n\nif ((sources == \"番茄\" || sources == \"七猫\" || sources == \"塔读\") && getArguments(source.getVariable(), \"fqpara\") == \"on\" && tab == \"小说\") {\n var fqssionid = getArguments(source.getVariable(), \"fqssionid\") || \"\";\n content = content\n .replace(/ident=\"/g, 'ident=\"' + base_url)\n .replace(/book_id=/g, 'book_id=' + book_id + '&ssionid=' + fqssionid);\n if (device_type == \"苹果\") {\n content = paraForiOS(content, sources);\n } else {\n content = paraForAndroid(content, sources);\n }\n\n\n}\ndata = JSON.stringify({\n content: content,\n});\n\nif (device_type == \"安卓\" && (tab == \"短剧\" || tab == \"视频\" || sources == '毒舌影视')) {\n data = {\n content: `【右上角刷新】开启播放(下一集请切换下一章刷新)\\n播放直链\\n${content}`,\n };\n data = JSON.stringify(data);\n if (book.durChapterIndex === chapter.index) {\n let video_url = `${base_url}/online_video?book_id=${book_id}&source=${sources}&tab=${tab}`;\n if (sources == '毒舌影视') {\n video_url = content;\n };\n java.startBrowser(video_url, title);\n java.toast(\"正在加载视频...\");\n }\n\n}\ndata;\n</js>$.content",
"imageStyle": "TEXT"
},
"ruleExplore": {
"author": "$.author",
"bookList": "$.data",
"bookUrl": "<js>\nlet book_id = result.book_id;\nlet sources = result.source;\nlet tab = result.tab || '小说';\nlet url = result.toc_url || '';\n\nlet qtdetail = {\n book_id: book_id,\n sources: sources,\n tab: tab,\n url: url\n}\nqtdetail = java.base64Encode(JSON.stringify(qtdetail));\n`data:;base64,${qtdetail},{\"type\":\"qingtian\"}`\n</js>",
"coverUrl": "$.thumb_url",
"intro": "$.abstract",
"kind": "{{$.category}}\n{{$.score}}\n{{$.status}}\n{{$.source}}\n{{$.tags}}",
"lastChapter": "{{$.last_chapter_title}} • {{$.last_update_time}}",
"name": "$.book_name",
"wordCount": "$.word_number"
},
"ruleSearch": {
"author": "$.author",
"bookList": "$.data",
"bookUrl": "<js>\nlet book_id = result.book_id;\nlet sources = result.source;\nlet tab = result.tab || '小说';\nlet url = result.toc_url || '';\n\nlet qtdetail = {\n book_id: book_id,\n sources: sources,\n tab: tab,\n url: url\n}\nqtdetail = java.base64Encode(JSON.stringify(qtdetail));\n`data:;base64,${qtdetail},{\"type\":\"qingtian\"}`\n</js>",
"checkKeyWord": "我的26岁女房客@番茄",
"coverUrl": "$.thumb_url",
"intro": "$.abstract",
"kind": "{{$.status}},{{$.score}},{{$.tags}},{{$.last_chapter_update_time}}",
"lastChapter": "{{$.source}} {{$.last_chapter_title}}",
"name": "$.book_name##(别名:.*?",
"wordCount": "$.word_number"
},
"ruleToc": {
"chapterList": "<js>\nlet res = JSON.parse(java.hexDecodeToString(result));\nif (res.method) {\n res = Object.fromEntries(\n res.body\n .replace(\"source\", \"sources\")\n .split(\"&\")\n .map((query) => query.split(\"=\"))\n );\n res.url = \"\";\n}\nlet book_id = res.book_id;\njava.put('book_id', book_id);\nlet tab = res.tab;\nlet sources = res.sources;\nlet url = res.url;\nlet html = \"\";\nlet proxy = getArguments(source.getVariable(), \"proxy\");\nif (url != \"\" && proxy == \"本地\") {\n if (sources == '69书吧') {\n let ck69 = String(cookie.getCookie(url));\n let headers = {\n \"Cookie\": ck69\n };\n let op = JSON.stringify({\n \"headers\": headers\n });\n html = java.ajax(url + ',' + op);\n } else {\n html = java.ajax(url);\n }\n //java.log(html);\n if (html.includes(\"Just a moment...\") && sources == '69书吧') {\n cookie.removeCookie(url);\n var x = `https://www.69shuba.com`;\n java.longToast('需要真人验证,请进入任意书籍详情页过验证');\n var s = java.startBrowserAwait(x, \"需要真人验证,请进入任意书籍详情页过验证\").body();\n\n let ck69 = String(cookie.getCookie(url));\n let headers = {\n \"Cookie\": ck69\n };\n let op = JSON.stringify({\n \"headers\": headers\n });\n //java.log(op);\n html = java.ajax(url + ',' + op);\n //java.log(html);\n }\n};\nlet base_url = getArguments(source.getVariable(), \"server\");\nlet op = {\n method: \"POST\",\n body: {\n html: html\n }\n};\nop = JSON.stringify(op);\nlet varia = String(book.getVariable('custom'));\nif (varia == 'null') {\n varia = '';\n}\nvaria = JSON.stringify({\n 'custom': varia\n});\n// varia = java.base64Encode(varia);\n//java.log(`${base_url}/catalog?book_id=${book_id}&source=${sources}&tab=${tab}&variable=${varia},${op}`);\nlet data = java.ajax(\n `${base_url}/catalog?book_id=${book_id}&source=${sources}&tab=${tab}&variable=${varia},${op}`\n);\nlet device, device_type;\ntry {\n device = java.deviceID();\n device_type = \"苹果\";\n} catch (e) {\n try {\n device = java.androidId();\n device_type = \"安卓\";\n } catch (e) {\n device = \"\";\n device_type = \"安卓\";\n }\n}\n\nif (tab == \"小说\") {\n if (device_type == \"安卓\") {\n book.type = 8;\n } else {\n book.type = 0;\n }\n} else if (tab == \"听书\") {\n if (device_type == \"安卓\") {\n book.type = 32;\n } else {\n book.type = 1;\n }\n} else if (tab == \"漫画\") {\n if (device_type == \"安卓\") {\n book.type = 64;\n } else {\n book.type = 2;\n }\n} else if (tab == \"短剧\") {\n if (device_type == \"安卓\") {\n book.type = 8;\n } else {\n book.type = 3;\n }\n} else {\n if (device_type == \"安卓\") {\n book.type = 8;\n } else {\n book.type = 0;\n }\n}\ndata;\n</js>$.data",
"chapterName": "$.title",
"chapterUrl": "<js>\nlet tab = result.tab;\nlet sources = result.source;\nlet title = result.title;\nlet item_id = result.item_id;\nlet book_id = java.get(\"book_id\");\nlet url = result.toc_url || \"\";\nlet qtcontent = {\n book_id: book_id,\n item_id: item_id,\n title: title,\n sources: sources,\n tab: tab,\n url: url,\n};\nqtcontent = java.base64Encode(JSON.stringify(qtcontent));\nif (sources == '卷') {\n content_url = item_id\n} else if ((sources == \"番茄\" || sources == \"七猫\" || sources == \"塔读\") && tab == \"小说\") {\n var base_url = getArguments(source.getVariable(), \"server\") || \"\";\n var fqssionid = getArguments(source.getVariable(), \"fqssionid\") || \"\";\n let sourcess = sources.replace('svip_', '');\n content_url = `data:;base64,${qtcontent},{\"type\":\"qingtian3\",\"js\":\"book ? result : '${base_url}/get_review?book_id=${book_id}&item_id=${item_id}&ssionid=${fqssionid}&source=${sourcess}'\"}`;\n} else {\n content_url = `data:;base64,${qtcontent},{\"type\":\"qingtian3\"}`;\n}\n</js>",
"updateTime": "$.first_pass_time"
},
"searchUrl": "<js>\nlet base_url = getArguments(source.getVariable(), 'server');\nlet media;\nlet sources = getArguments(source.getVariable(), 'source');\nif (String(key).startsWith(\"m:\") || String(key).startsWith(\"m\")) {\n media = \"漫画\"\n key = key.slice(2)\n} else if (String(key).startsWith(\"t:\") || String(key).startsWith(\"t\")) {\n media = \"听书\"\n key = key.slice(2)\n} else if (String(key).startsWith(\"d:\") || String(key).startsWith(\"d\")) {\n media = \"短剧\"\n key = key.slice(2)\n} else if (String(key).startsWith(\"x:\") || String(key).startsWith(\"x\")) {\n media = \"小说\"\n key = key.slice(2)\n} else {\n media = getArguments(source.getVariable(), 'media');\n}\nif (key.includes('@')) {\n var parts = key.split('@');\n key = parts[0];\n sources = parts[1] || sources;\n}\nlet qtcookie = cookie.getCookie(base_url);\nlet op = {\n method: \"GET\",\n headers: {\n cookie: qtcookie\n },\n};\nop = JSON.stringify(op);\n`${base_url}/search?title=${key}&tab=${media}&source=${sources}&page={{page}},${op}`\n</js>",
"weight": 0
},
{
"bookSourceComment": "00shu小说网书源编码GBK适配章节/搜索/详情解析",
"bookSourceGroup": "CSS; 正则",
"bookSourceName": "00shu小说",
"bookSourceType": 0,
"bookSourceUrl": "https://www.00shu.la",
"bookUrlPattern": "",
"customOrder": 0,
"enabled": true,
"enabledExplore": false,
"exploreUrl": "",
"lastUpdateTime": "1764478609406",
"loginUrl": "",
"ruleBookInfo": {
"author": "##:author\"[^\"]+\"([^\"]*)##$1###",
"coverUrl": "##og:image\"[^\"]+\"([^\"]*)##$1###",
"intro": "##:description\"[^\"]+\"([\\w\\W]*?)\"/##$1###",
"kind": "##:category\"[^\"]+\"([^\"]*)##$1###",
"lastChapter": "##_chapter_name\"[^\"]+\"([^\"]*)##$1###",
"name": "##:book_name\"[^\"]+\"([^\"]*)##$1###",
"tocUrl": ""
},
"ruleContent": {
"content": "@css:#content p@textNodes##广告|弹窗|手机阅读|记住网址|<!\\[CDATA\\[|\\]\\]>",
"nextContentUrl": ""
},
"ruleExplore": [],
"ruleSearch": {
"author": "@css:.s2>a@text",
"bookList": "@css:div.result-list>ul>li",
"bookUrl": "@css:.s1>a@href",
"coverUrl": "@css:.s3>img@src",
"intro": "@css:.s4@text",
"kind": "@css:.s5@text",
"lastChapter": "@css:.s6@text",
"name": "@css:.s1>a@text"
},
"ruleToc": {
"chapterList": "-:<li><a[^\"]+\"([^\"]*)\">([^<]*)",
"chapterName": "$2",
"chapterUrl": "$1",
"nextTocUrl": ""
},
"searchUrl": "/search.aspx?key={{key}}&page={{page}},{\"charset\": \"gbk\"}",
"weight": 0
},
{
"bookSourceComment": "",
"bookSourceName": "22笔趣阁pc",
"bookSourceType": 0,
"bookSourceUrl": "https://www.22biqu.com",
"customOrder": 0,
"enabled": true,
"enabledCookieJar": true,
"enabledExplore": true,
"exploreUrl": "[\n{\n \"title\": \"♕ 分类 ♕\",\n \"url\": \"\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n },\n {\n \"title\": \"玄幻魔法\",\n \"url\": \"/fenlei/1_{{page}}.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n },\n {\n \"title\": \"武侠修真\",\n \"url\": \"/fenlei/2_{{page}}.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n },\n {\n \"title\": \"都市言情\",\n \"url\": \"/fenlei/3_{{page}}.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n },\n {\n \"title\": \"历史军事\",\n \"url\": \"/fenlei/4_{{page}}.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n },\n {\n \"title\": \"科幻灵异\",\n \"url\": \"/fenlei/6_{{page}}.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n },\n {\n \"title\": \"游戏竞技\",\n \"url\": \"/fenlei/5_{{page}}.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n },\n {\n \"title\": \"女生耽美\",\n \"url\": \"/fenlei/7_{{page}}.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n },\n {\n \"title\": \"其他类型\",\n \"url\": \"/fenlei/8_{{page}}.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n },\n {\n \"title\": \"♕ 排行榜 ♕\",\n \"url\": \"\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n },\n {\n \"title\": \"总排行榜\",\n \"url\": \"/rank/allvisit/\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n },\n {\n \"title\": \"月排行榜\",\n \"url\": \"/rank/monthvisit/\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n },\n {\n \"title\": \"周排行榜\",\n \"url\": \"/rank/weekvisit/\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n },\n {\n \"title\": \"收 藏 榜\",\n \"url\": \"/rank/goodnum/\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n },\n {\n \"title\": \"♕ 全本 ♕\",\n \"url\": \"\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n },\n {\n \"title\": \"玄幻魔法\",\n \"url\": \"/quanben/fenlei/1_{{page}}.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n },\n {\n \"title\": \"武侠修真\",\n \"url\": \"/quanben/fenlei/2_{{page}}.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n },\n {\n \"title\": \"都市言情\",\n \"url\": \"/quanben/fenlei/3_{{page}}.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n },\n {\n \"title\": \"历史军事\",\n \"url\": \"/quanben/fenlei/4_{{page}}.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n },\n {\n \"title\": \"科幻灵异\",\n \"url\": \"/quanben/fenlei/6_{{page}}.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n },\n {\n \"title\": \"游戏竞技\",\n \"url\": \"/quanben/fenlei/5_{{page}}.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n },\n {\n \"title\": \"女生耽美\",\n \"url\": \"/quanben/fenlei/7_{{page}}.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n },\n {\n \"title\": \"其他类型\",\n \"url\": \"/quanben/fenlei/8_{{page}}.html\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n }\n]",
"header": "{\n \"User-Agent\": \"Mozilla/5.0 (Linux; Android 8.0.0; MI 5s Plus Build/OPR1.170623.032; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.99 Mobile Safari/537.36\"\n}",
"lastUpdateTime": 1764641625153,
"respondTime": 180000,
"ruleBookInfo": {
"author": "[property=\"og:novel:author\"]@content",
"coverUrl": "[property=\"og:image\"]@content",
"intro": "[property=\"og:description\"]@content",
"kind": "[property~=category|status|update_time]@content",
"lastChapter": "[property~=las?test_chapter_name]@content",
"name": "[property=\"og:novel:book_name\"]@content",
"tocUrl": ""
},
"ruleContent": {
"content": "#content@p@html##本章未完.*",
"nextContentUrl": "text.下一页@href"
},
"ruleExplore": {
"author": ".author@text##作者:|\\d.*",
"bookList": ".hot_sale",
"bookUrl": "a@href",
"coverUrl": "a.0@href##.+\\D((\\d+)\\d{3})\\D##https://img.22biqu.com/$2/$1/$1s.jpg###",
"intro": ".review@text",
"name": ".title@text##^\\d+\\."
},
"ruleSearch": {
"author": ".s4@text",
"bookList": ".txt-list.txt-list-row5 > li:not(:first-child)",
"bookUrl": "a.0@href",
"coverUrl": "",
"kind": ".s1@text",
"lastChapter": ".s3@text",
"name": ".s2@text",
"checkKeyWord": "系统"
},
"ruleToc": {
"chapterList": "class.section-list fix@li@a",
"chapterName": "text",
"chapterUrl": "href",
"nextTocUrl": "option@value"
},
"searchUrl": "{{cookie.removeCookie(source.key)}}/ss/,{\n \"body\": \"searchkey={{key}}&submit=搜索\",\n \"method\": \"POST\"\n}",
"weight": 0
}
]