- showCmt 能力检测:魔改版(有 showBrowser)原地弹窗(点遮罩关闭/80%高度),官方版降级 startBrowser - 弹窗预载 ajax 强制 https,规避反代 301 拿到空内容 - jsLib 函数不再引用规则变量 baseUrl(作用域隔离),fqTab 显式传参 - bookList 返回数组对象;ruleBookInfo 以 init 提取 data;searchUrl 改用局部变量并 encodeURIComponent
63 lines
55 KiB
JSON
63 lines
55 KiB
JSON
[
|
||
{
|
||
"bookSourceName": "🔅番茄聚合",
|
||
"bookSourceGroup": "📖番茄小说",
|
||
"bookSourceType": 0,
|
||
"bookSourceUrl": "https://fq.20010117.xyz",
|
||
"bookSourceComment": "番茄聚合书源(fanqie-go 后端)\n\n基础使用教程:\n1、搜索:\n 1) 基础搜索:直接搜,默认搜索当前模式内容\n 2) 快捷搜索:使用特殊关键词(x,t,m,d)搜索\n x:书名 如(x:十日终焉) 搜小说(可省略)\n t:书名 如(t:十日终焉) 搜听书\n m:书名 如(m:十日终焉) 搜漫画\n d:书名 如(d:十日终焉) 搜短剧\n 冒号支持中文英文,也可在书源设置中切换默认搜索模式\n2、发现页:\n 顶部筛选选择 类型/频道/字数/更新/排序 后,点击分类宫格浏览,下拉翻页\n3、段评:\n 阅读正文时点击段尾气泡即可查看段落评论,样式可在段评设置中调整\n4、听书/漫画/短剧:\n 听书自动用播放器播放;漫画走服务端解密代理;短剧打开章节自动唤起播放\n\n更换服务器地址:只需修改源URL(bookSourceUrl),全部规则使用相对地址自动跟随",
|
||
"bookSourceEnabled": true,
|
||
"enabledExplore": true,
|
||
"enabledCookieJar": true,
|
||
"customOrder": 0,
|
||
"weight": 0,
|
||
"respondTime": 180000,
|
||
"lastUpdateTime": 1791607295124,
|
||
"bookUrlPattern": "https?:\\/\\/(?:[a-zA-Z0-9-]+\\.)?[a-zA-Z0-9-]+(?::\\d+)?\\/detail\\?book_id=\\d+.*",
|
||
"jsLib": "// 当前书源版本号\nlet localVersion = '26.10.10';\n\n// 初始配置\nconst defaultConfig = {\n 发现页类型: \"小说\",\n 频道: \"男频\",\n 搜索模式: \"小说\"\n};\n\n// 获取源变量\nfunction getVariable(k) {\n if (k == undefined) k = \"\";\n const { source } = this;\n let parsed = {};\n try {\n parsed = JSON.parse(source.getVariable());\n } catch {}\n if (k == \"\") {\n return parsed;\n }\n let value = parsed[k];\n if (value == undefined) {\n value = defaultConfig[k];\n }\n return value != undefined ? value : \"\";\n}\n\n// 设置源变量\nfunction setVariable(k, v, t) {\n if (t == undefined) t = true;\n const { source, java } = this;\n const vs = this.getVariable();\n vs[k] = v;\n source.setVariable(JSON.stringify(vs, null, 4));\n if (k != '搜索关键词' && t) {\n java.toast(`\\n设置 ${k} 为 ${v}`)\n }\n}\n\n// 还原\nfunction deleteVariable() {\n const { source, java } = this;\n source.setVariable('');\n java.longToast('\\n所有设置已还原到初始化导入状态');\n}\n\n// 从 URL 提取内容类型标记(n小说/t听书/m漫画/d短剧)。\n// 注意:jsLib 函数作用域看不到规则变量 baseUrl,必须由调用方显式传入\nfunction fqTab(url, def) {\n if (def == undefined) def = 'n';\n let m = String(url).match(/fqtab=([ntmd])/);\n return m ? m[1] : def;\n}\n\n// 判断当前环境\nfunction checkEnv() {\n const {\n java,\n source\n } = this;\n\n try {\n java.qread();\n return \"轻阅读\";\n } catch (e) {}\n\n try {\n if (typeof java.reLoginView == 'function') {\n return \"改版\";\n }\n new Packages.io.legato.kazusa.utils.TimeoutCancellationException('');\n return \"改版\";\n } catch (e) {}\n try {\n java.deviceID();\n return \"苹果\";\n } catch (e) {}\n\n if (typeof source.loginUi == 'function') {\n return \"安卓\";\n }\n\n return \"改版\";\n}\n\n// 备用设置方案(登录页按钮)\nfunction ste(v) {\n const { java } = this;\n let moreSettings = this.getVariable(\"更多设置\") || {};\n let paraSettings = this.getVariable(\"段评设置\") || {};\n if (v == \"段评\") {\n if (paraSettings[\"段评开关\"] != \"false\") {\n paraSettings[\"段评开关\"] = \"false\";\n java.longToast(\"\\n❌段评已关闭\");\n } else {\n paraSettings[\"段评开关\"] = \"true\";\n java.longToast(\"\\n✅段评已开启\");\n }\n }\n if (v == \"小说\" || v == \"听书\" || v == \"漫画\" || v == \"短剧\") {\n moreSettings[\"搜索模式\"] = v;\n java.longToast(`\\n已设置搜索模式为 ${v}`);\n }\n if (v == \"图片\") {\n if (moreSettings[\"显示图片\"] != \"false\") {\n moreSettings[\"显示图片\"] = \"false\";\n java.longToast(\"\\n❌已关闭图片显示\");\n } else {\n moreSettings[\"显示图片\"] = \"true\";\n java.longToast(\"\\n✅已开启图片显示\");\n }\n }\n if (v == \"简介\") {\n if (moreSettings[\"完整简介\"] != \"false\") {\n moreSettings[\"完整简介\"] = \"false\";\n java.longToast(\"\\n❌已关闭完整简介\");\n } else {\n moreSettings[\"完整简介\"] = \"true\";\n java.longToast(\"\\n✅已开启完整简介\");\n }\n }\n this.setVariable(\"更多设置\", moreSettings, false);\n this.setVariable(\"段评设置\", paraSettings, false);\n}\n\n// 按钮切换时改变源变量并刷新发现\nfunction show(m, t) {\n const { java, source } = this;\n let data = {};\n try {\n data = JSON.parse(source.getVariable())\n } catch {}\n data[t] = m;\n if (t == \"发现页类型\") {\n data[\"频道\"] = '男频';\n }\n source.setVariable(JSON.stringify(data, null, 2));\n java.refreshExplore();\n}\n\nfunction createFilter(title, chars, defaultVal, paramKey, size, viewName) {\n if (viewName == undefined) viewName = \"'请选择类型'\";\n return {\n title: title,\n type: \"select\",\n chars: chars,\n default: defaultVal,\n \"viewName\": viewName,\n action: `show(infoMap['${title}'],'${paramKey}')`,\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: size\n }\n };\n}\n\nfunction createText(title, action, defaultValue, size, viewName) {\n return {\n title: title,\n type: 'text',\n default: defaultValue || '',\n action: action,\n viewName: viewName || title,\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: size\n }\n };\n}\n\nfunction createButton(title, action, size) {\n return {\n title: title,\n type: 'button',\n action: action,\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: size\n }\n };\n}\n\nfunction exploreSearch() {\n let {java, source} = this;\n var key = this.getVariable('搜索关键词');\n var tab = this.getVariable('发现页类型') || '小说';\n if (!key) {\n try { java.toast('请输入搜索关键词'); } catch (e) {}\n return;\n }\n var tabMap = {\n '小说': 'x:',\n '漫画': 'm:',\n '听书': 't:',\n '短剧': 'd:',\n '': ''\n };\n if (['t:', 'x:', 'd:', 'm:', 't:', 'x:', 'd:', 'm:'].some(p => key.startsWith(p))) {\n tab = '';\n }\n key = `${tabMap[tab]}${key}`;\n try {\n java.searchBook(key, source.bookSourceName + \"::\" + source.getKey());\n } catch (e1) {\n try { java.searchBook(key); } catch (e2) {\n try { java.open('search', key); } catch (e3) { java.toast('搜索失败,请手动搜索'); }\n }\n }\n java.refreshExplore();\n}\n\n//去除图片\nfunction removeAllImgTags(htmlString) {\n const imgTagRegex = /<img\\b[^>]*>|<\\/img>|<img\\b[^>]*\\/>/gi;\n return htmlString.replace(imgTagRegex, '');\n}\n\nlet svgs = [\n \"<svg t='1760002253572' class='icon' viewBox='-150 0 1224 1224' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='1490' width='800' height='800'><g transform='rotate(90 512 512) scale(1 1.2)'><path d='M224 149.333333h576c40.533333 0 74.666667 34.133333 74.666667 74.666667v460.8c0 40.533333-34.133333 74.666667-74.666667 74.666667h-108.8c-40.533333 0-78.933333 17.066667-104.533334 46.933333l-49.066666 55.466667c-4.266667 6.4-14.933333 10.666667-23.466667 10.666667-10.666667 0-19.2-4.266667-25.6-10.666667l-46.933333-55.466667c-25.6-29.866667-64-46.933333-104.533334-46.933333h-110.933333c-40.533333 0-74.666667-34.133333-74.666667-74.666667V224c0-40.533333 34.133333-74.666667 74.666667-74.666667z' fill='$tccolor' p-id='1529'></path><path d='M512 938.666667c-27.733333 0-55.466667-12.8-72.533333-34.133334l-46.933334-55.466666c-14.933333-17.066667-34.133333-25.6-57.6-25.6h-110.933333c-76.8 0-138.666667-61.866667-138.666667-138.666667V224C85.333333 147.2 147.2 85.333333 224 85.333333h573.866667c76.8 0 138.666667 61.866667 138.666666 138.666667v460.8c0 76.8-61.866667 138.666667-138.666666 138.666667h-108.8c-21.333333 0-42.666667 8.533333-55.466667 25.6l-49.066667 55.466666c-17.066667 21.333333-44.8 34.133333-72.533333 34.133334zM224 149.333333C183.466667 149.333333 149.333333 183.466667 149.333333 224v460.8c0 40.533333 34.133333 74.666667 74.666667 74.666667h110.933333c40.533333 0 78.933333 17.066667 104.533334 49.066666l46.933333 55.466667c6.4 6.4 14.933333 10.666667 25.6 10.666667 8.533333 0 19.2-4.266667 23.466667-10.666667l49.066666-55.466667c25.6-29.866667 64-46.933333 104.533334-46.933333h108.8c40.533333 0 74.666667-34.133333 74.666666-74.666667V224C874.666667 183.466667 840.533333 149.333333 800 149.333333h-576z' fill='$color' p-id='1530'></path> </g><text x='460' y='650' font-family='Arial, sans-serif' text-anchor='middle' font-size='400' fill='$fontcolor'>$displayText</text></svg>\",\n\n \"<svg xmlns='http://www.w3.org/2000/svg' width='100' height='90' viewBox='0 0 25 17' fill='none' style='color: $color; opacity: 1;'><path stroke='currentColor' stroke-width='1.00' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='4.00' style='stroke: $color; stroke-opacity: 0.5; stroke-width: 1px; stroke-linejoin: round; fill: $tccolor; fill-opacity: 0.5;' d='M 7.5 0.5 L 16.5 0.5 A 7 7 0 0 1 23.5 7.5 L 23.5 7.5 A 7 7 0 0 1 16.5 14.5 L 8.5 14.5 L 5.192 16.5 L 5.5 14.5 A 7 7 30 0 1 0.5 7.5 L 0.5 7.5 A 7 7 0 0 1 7.5 0.5 Z'/><text fill='$fontcolor' font-family='Arial, sans-serif' font-size='10' font-weight='500' text-anchor='middle' dy='0.35em' x='12.00' y='7.50'>$displayText</text></svg>\",\n\n \"<svg xmlns='http://www.w3.org/2000/svg' width='100' height='90' viewBox='0 0 25 17' fill='none' style='color: $color; opacity: 1;'><path d='M24 14.5v-12a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v7.528a2 2 0 0 1-.211.894l-2.065 4.13a1 1 0 0 0 .894 1.448H22a2 2 0 0 0 2-2z' stroke='currentColor' style='stroke: $color; stroke-opacity: 0.5; stroke-width: 1px; stroke-linejoin: round; fill: $tccolor; fill-opacity: 0.5;'></path><text x='13.5' y='12' text-anchor='middle' alignment-baseline='auto' font-size='10' fill='currentColor' font-family='Arial, sans-serif' font-weight='500' style='fill: $fontcolor; opacity: 1;'>$displayText</text></svg>\",\n\n\n\n \"<svg xmlns='http://www.w3.org/2000/svg' width='100' height='90' viewBox='6 10 88 76' style='color: $color; opacity: 1;'><path d='M12,12 L88,12 Q92,12 92,16 L92,68 Q92,72 88,72 L28,72 L12,84 L12,72 Q8,72 8,68 L8,16 Q8,12 12,12 Z' stroke='currentColor' stroke-width='4' stroke-linejoin='round' style='stroke: $color; stroke-opacity: 0.5; stroke-width: 2.5px; stroke-linejoin: round; fill: $tccolor; fill-opacity: 0.5;'></path><text x='50' y='60' font-family='Arial, sans-serif' text-anchor='middle' font-size='45' fill='currentColor' font-weight='500' alignment-baseline='auto' style='fill: $fontcolor; opacity: 1;'>$displayText</text></svg>\",\n\n\n\n \"<svg xmlns='http://www.w3.org/2000/svg' width='100' height='90' viewBox='0 0 25 17' fill='none' style='color: $color; opacity: 1;'><rect x='0.5' y='0.5' width='24' height='16' rx='2.5' ry='2.5' stroke='currentColor' style='stroke: $color; stroke-opacity: 0.5; stroke-width: 1px; stroke-linejoin: round; fill: $tccolor; fill-opacity: 0.5;'></rect><text x='12.5' y='12' text-anchor='middle' alignment-baseline='auto' font-size='10' fill='currentColor' font-family='Arial, sans-serif' font-weight='500' style='fill: $fontcolor; opacity: 1;'>$displayText</text></svg>\",\n\n\n\n\n `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 216 200'>\n <path fill='#802622' d='M110 0h14l1 1h8v1h4v1h4v1h4v1h3v1h2l1 1h2l1 1h1l1 1h2v1h2v1h2v1h2v1h1v1h2v1h2v1h1v1h1l1 1h1v1h1v1h2v1h1v1h1l1 1v1h2v1h1v1h1v1l1 1h1v1h1v1h1v1h1v2h1v1h1v1h1v1h1v1h1v2h1v1h1v1l1 1v1h1v2h1v1h1v2h1v1l1 1v1l1 1v1h1v2h1v2h1v2h1v3h1v2h1v3a5 5 0 0 0 1 2h1v1h-1v4h1v4h1v5h1v8h1v18h-1v7h-1v5h-1v2h-1l-1 2v3h-1v2l-1 1v3h-1v2h-1v2h-1v2h-1v2h-1v2h-1v2h-1v1l-1 1v1h-1v2h-1v1l-1 1v1h-1v1h-1v1h-1v2h-1l-1 1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1l-1 1h-1v1h-1l-1 1h-1 1v-1h1v-1h1v-1h1a1 1 0 0 1 0-1 2 2 0 0 1 1-1h1v-2h1v-1h1v-1h1v-1h1v-1l1-1v-1h1v-1h1v-1l1-1h1v-2h1v-1l1-1v-1h1v-1l1-1v-1h1v-2h1v-1l1-1v-2h1v-2h1v-2h1v-2h1v-3h1v-5h1v-4l1-1v-6h1v-11h1v-2h-1v-1h1v-1h-1 1v-2h-1v-7h-1v24h-1v3h-1v1h-1v4h-1v4h-1v3h-1v2h-1v3l-1 1v1h-1v2l-1 1-1 2v1h-1v1h-1v2h-1v2h-1v1h-1v2h-1v1h-1v1h-1v1h-1v2h-1v1h-2v2h-1l-1 1v1h-2v1h-1v1h-1v1h-1v1h-1l-1 1-1 1h-2v1h-1v-1h1v-1h2v-1h1v-1h1v-1h1v-1h1v-1h1v-1l1-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1l1-1v-1h1v-1h1v-2l1-1v-1h1v-1h1v-2l1-1v-1h1v-2l1-1v-1h1l-1-1h1v-2h1v-2l1-1v-3h1v-3l1-1v-3h1v-4l1-1v-10h1V98h-1v-9h-1v-4l-1-1v-4l-1-1v-4h-1v-3h-1a6 6 0 0 0 0-3h-1v-1l-1-1v-2h-1v-2h-1v-1h-1v-2h-1v-1h-1v-1l-1-1v-1h-2v-1 2a2 2 0 0 1-1-1v-2h-1v-1h-1v-2h-1v-1l-1-1-1-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1l-1-1h-1v-1l-1-1h-1v-1h-1v-1h-1v-1h-1v-1h-1l-1-1v-1h-2v-1h-1v-1h-1v-1h-2v-1a1 1 0 0 1-1 0v-1h-2v-1h-1l-1-1h-1v-1h-2v-1h-2v-1h-2v-1h-1v-1h-4v-1h-1v-1h-2l-1-1h-2v-1h-5v-1a1 1 0 0 1-1-1v1h-1v-1 1h-2l-1-1h-3v-1h-23 1-2v1h-1v-1h-2v1h-4v1h-2v2l1-1h4v-1h4v-1h23v1h6v1h4v1h4v1h3v1h3v1h2l1 1h2l1 1 1 1h1v1h1l2 1h2v1h1v1-1 1h2v1h1v1h2v1h1v1h1v1h1v1h1v1h1l1 1h1v1h1v2h1l1 1 1 1v1h1v1h1v1h1v2l1 1v1h1v1h1v1h1v1h1v2l1 1v1h1v2h1v1l1 1v1h1v2l1 1v2h1v2h1v1l1 2v3h1v4l1 1v5h1v9l1 1v6h-1v8h-1v1-16h-1v-2h1v-2h-1v-5h-1v-4h-1v-3l-1-1v-3a2 2 0 0 1-1-1v-2l-1-1v-1h-1v-3h-1v-1h-1v-2l-1-1v3h1v3h1v4h1v3l1 1v3h1v5h1v25h-1v6h-1v4l-1 1v2h-1v2h-1v3h-1v2h-1v2l-1 1v1h-1v2h-1v2h-1v2h-1v1h-1v2l-1 1-1 1v1h-1v1h-1v2h-1l-1 1v1h-1v1h-1v1h-1v1h-1v1l-1 1-1 1h-1v1h-1v1h-1v1h-1l-1 1h-1v1h-1v-1h1v-1h1v-1l1-1h1v-1l2-1v-1h1v-1l1-1v-1h1v-1h1v-1h1v-2h1v-1l1-1v-1h1v-1l1-1v-2h1v-2h1v-1l1-1v-1h1v-3h1v-2h1v-1h1v-2l1-1v-2h1v-3h1v-5h1v-5h1v5-21h-1v-6h-1v-5h-1v-3h-1v-3h-1v-2h-1v-3h-1v-1l-1-1v-2h-1v-1l-1-3h-1v-1h-1a1 1 0 0 1 0-1v-1h-1v-2h-1v-1h-1v-1l-1-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1l-1-1v-1h-2v-1h-1v-1h-1v-1h-1l-1-1h-1v-1h-1v-1h-1l-1-1h-1v-1h-2v-1h-2v-1h-1v-1h-2a2 2 0 0 0-1-1h-1v-1h-2l-1-1h-2v-1h-3v-1h-2l-2-1h-2v-1h-5v-1h-2 3-22v1h-5v1h-4v1h-2l-2 1h-2v1h-2l-1 1h-2v1h-1l-1 1h-1v1h-2v1h-1l-1 1h-1v1h-1l-1 1h-1v1h-1v1h-1l-1 1-1 1-1 1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v2h-1v1h-1v1h-1v1h-1v2h-1v1h-1v1h-1v2h-1v1l-1 1v1h-1v2h-1v2h-1v2h-1v3h-1v3h-1v3l-1 1v3l-1 1v6h-1v12h1v5l1 3v3h1v4h1v3h1v3h1v3h1v2l1 1v1h1v2l1 2v1h1v1l1 1v1h1v1h1v2h1v1h1v2h1v1h1v1h1v1h1v2h1v1h1l1 1h1v1h1v1l1 1h1v1h2v1h1v1h1v1h2v1h2v1h1v1h1v1h2v1h7v1h1v2h1v2h-4l-3-1v1h1a3 3 0 0 1 2 1h1v1-1 1h1v-1h1v2h5v1h1v1h1v1h2v1h-2l1 1v1h-1 1v3h-1v2h-2 1v1h1v1h-1v2h-1 1v3h-1l-1 1h-1v1h-3v1h-2v-1h-1l-1-1h-1v-1 1h-5v1h-4v-1l-1-1h-1v1h-2v-1h-1v-1h-1v2h2v1h2v1h2l1 1h2v1h1v1h2v1h1v1h5v1h1v1h1v1l-1 1h-4v-1h-2v-1h-3v-1h-5v-1h-1v-1h-1v1h-1a1 1 0 0 1 0-1l1-1h1-1v-1h-2a2 2 0 0 0-1-1h-1v-1h-2v-1h-2v-1h-3v-1h-1v-1h-2v-1h-1v-1h-1v-1h-2v-1h-2v-1h-1v-1h-1v-1h-1v-1h-2v-1a2 2 0 0 1-1-1h-1v-1h-1v-1h-1v-1h-1v-1h-1l-1-1v-1h-1v-1h-1v-1h-1a1 1 0 0 1 0-1h-1v-1l-1-1-1-1v-1h-1v-1h-1v-1h-1v-1h-1v-2h-1v-1h-1v-1l-1-1v-1h-1v-2h-1v-1h-1v-2h-1v-1l-1-1v-1h-1v-2h-1v-1h-2v-2a1 1 0 0 1-1 0v-2h-1v-1h-1l-1 1v1h-1v-1h-1l-1-2h1v-5h-1v-4a2 2 0 0 1-1-1v-3H9v-3a2 2 0 0 1-1-1v-2H7v-3H6v-7H5v-1h1v-1h1v-5l-1-2v-7l1-2v-6h1v-1H7h2v1H8v8H7v9h1V91h1v-7h1v-4h1v-1a3 3 0 0 1 0-2h-1v-1h1l1-1-1-1h1v-2h1v-3h1v-6h1v-2h1v-1 1-1h1-1v-1h1v-1h1v-1h-1v-1h1v-1h1-1v-1h1v-2h1v-2h1v-1h-1v2h-1v1h-1v2h-1v-1l1-1v-2h1v-1h1v-1l1-1v-2h1v-1h1v-3h1v-1h2v-1h1v-1h1v-2h1v-1h1v-1h1v-2h1l1-1v-1h1v-1h1v-2h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1l1-1h1v-1h1v-1h1v-1h1v-1h1l1-1h1v-1h1l1-1 1-1v1-1h1V8h2V7h1l1-1h1V5h2V4h1l1-1h2V2h2V1h2V0h1v1h-1l-1 1h-1v1h-2v1h-3v1h-1v1h-2v1h-1v1h-2v1h-2v1h-1v1h-1l-1 1-1 1h-1l-1 1h-1v1h-1v1h-1v1h-1v1h-1v1h-1l-1 1h-1v2h-1l-1 1v1h-2v1h-1v2h-1v1h-1v1h-1v1h-1v1l-1 1v1h-1v1h-1v1l-1 1v1h-1v2h-1v1h-1v2h-1v1h1v1h-1v1l-1 1h-1v1h-1v2l-1 1v2h-1v2h-1v2h-1v2h-1v3h-1v3h-1v3h-1v2l-1 1v3h-1v4H9v6H8v6H7v11-5h1v-3l1-2v-3h1v-4h1v-5l1-1v-3h1v-4h1v-3h1v-3h1v-3l1-2h1v-3h1v-1l1-1v-2h1v-1h1v-2h1v-1h1v-1l1-1v-1l1-1v-1h1v-1h1v-1h1v-1h1v-2h1v-1h1v-1h1v-1h1v-1h1v-2h1v-1h1l1-1h1v-1l1-1 1-1h1v-1h1v-1h1v-1h1v-1h1v-1h2v-1h1v-1h1v-1h2v-1h1l1-1v-1h2v-1h1l1-1h1v-1h2v-1h2v-1h2v-1h1l2-1h1V8h2V7h3V6h3V4h6V3h4V2h4l1-1h7V0h8z'/>\n <text x=\"115\" y=\"130\" font-family=\"Arial, sans-serif\" text-anchor=\"middle\" font-size=\"80\" font-weight=\"700\" fill='#802632'>$displayText</text>\n</svg>`\n\n];\n\n// 创建svg\nfunction createSvg(number, encodedUrl, sources) {\n var displayText = number > 99 ? \"99+\" : number.toString();\n let data = this.getVariable('段评设置');\n let svgstyle = data['段评样式'] || \"0\";\n let color = data['段评边框颜色'] || \"#A7A7A7\";\n let tccolor = data['段评填充颜色'] || \"transparent\";\n let fontcolor = data['段评字体颜色'] || \"#A7A7A7\";\n let zdy = svgs[0];\n try {\n zdy = data['自定义段评样式'][svgstyle] || svgs[0];\n } catch {}\n const styleMap = {\n \"0\": svgs[0],\n \"1\": svgs[1],\n \"2\": svgs[2],\n \"3\": svgs[3],\n \"4\": svgs[4],\n \"5\": svgs[5],\n };\n let svg = styleMap[svgstyle] || zdy;\n svg = svg.replace('$color', color)\n .replace('$tccolor', tccolor)\n .replace('$fontcolor', fontcolor)\n .replace('$displayText', displayText);\n var jc = 'js';\n let islyc = this.checkEnv()\n if (islyc == \"改版\") {\n jc = 'click'\n }\n let style = data['气泡增大'] == \"true\" ? 'TEXT' : 'text';\n var encodedSvg = this.java.base64Encode(svg);\n return `data:image/svg+xml;base64,${encodedSvg},{\"${jc}\":\"showCmt('${encodedUrl}', '${sources}' ,'段评')\",\"style\":\"${style}\"}`;\n}\n\n\n// 段评气泡注入(正文 <p> 支持任意属性)\nfunction paraForAndroid(content, sources) {\n let { cache } = this;\n const createSvg = this.createSvg.bind(this);\n return content.replace(/(<p[^>]*>)([\\s\\S]*?)(?:<comment ident=\"([^\"]*)\" count=\"([^\"]*)\"\\s*\\/>)?(<\\/p>)/g,\n function (match, open, text, url, count, close) {\n if (url && count) {\n cache.putMemory(url, 0);\n return `<p>${text}<img src=\"${createSvg(count, url, sources)}\"></p>`;\n }\n return `<p>${text}</p>`;\n });\n}\n\n//源阅(iOS)段评\nfunction paraForiOS(content, sources) {\n return content.replace(/(<p[^>]*>)([\\s\\S]*?)(?:<comment ident=\"([^\"]*)\" count=\"([^\"]*)\"\\s*\\/>)?(<\\/p>)/g,\n function (match, open, text, url, count, close) {\n if (url && count) {\n const numCount = Number(count);\n const displayCount = !isNaN(numCount) && numCount > 99 ? '99' : count;\n return `<div rs-native>${text}<comment count=\"${displayCount}\" onPress=\"java.showReadingBrowser('${url.replace(/&/g, '&')}','${sources}段评')\"></div>`;\n }\n return `<p>${text}</p>`;\n });\n}\n\n// 显示段评:优先魔改版(阅读R/Sigma等)的原地底部弹窗(点击遮罩关闭、可拖拽下滑);\n// 官方版无此 API,自动降级为 startBrowser 打开自包含评论页\nfunction showCmt(url, sources, ident) {\n let { java } = this;\n if (ident == null) ident = '段评';\n var title = (sources || '番茄') + ident;\n var hasPopup = false;\n try { if (typeof java.showBrowser == 'function') hasPopup = true; } catch (e) {}\n if (hasPopup) {\n try {\n let html = '';\n // 正文注入的 ident 可能是 http://(反代 301 到 https),ajax 对 301 处理不稳,强制 https\n try { html = String(java.ajax(url.replace(/^http:\\/\\//, 'https://'))); } catch (e) {}\n java.showBrowser(url, html, 'window.java=java;', JSON.stringify({\n expandedCornersRadius: 20,\n dismissOnTouchOutside: true,\n isDraggable: true,\n shouldDimBackground: true,\n backgroundDimAmount: 0.5,\n hardwareAccelerated: true,\n isNestedScrollingEnabled: true,\n setFitToContents: true,\n isHideable: true,\n heightPercentage: 0.8\n }));\n return;\n } catch (e) {}\n }\n try {\n java.startBrowser(url, title);\n return;\n } catch (e) {}\n try { java.startBrowserDp(url, title); } catch (e2) { java.toast('当前版本不支持查看评论'); }\n}\n\n// ============ 书源设置页 ============\nfunction getHtmlSettings() {\n const { java } = this;\n let data = this.getVariable('更多设置');\n let html = `<!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,maximum-scale=1,user-scalable=no\">\n<title>番茄聚合 - 书源设置</title>\n<style>\n* { margin:0; padding:0; box-sizing:border-box; -webkit-tap-highlight-color:transparent; }\n:root {\n --color-primary:#2D9D78; --color-primary-light:#3DB893;\n --bg-card:rgba(255,255,255,.98); --bg-elevated:#fafbfc;\n --text-primary:#1a1d23; --text-secondary:#6b7280;\n --border-color:rgba(0,0,0,.08);\n}\nhtml,body { width:100%; min-height:100vh; font-family:-apple-system,BlinkMacSystemFont,sans-serif; font-size:16px; color:var(--text-primary); background:linear-gradient(135deg,#eef2ff,#f5f7ff); }\n.container { padding:16px; max-width:600px; margin:0 auto; }\n.main-card { background:var(--bg-card); border-radius:20px; box-shadow:0 4px 24px rgba(0,0,0,.1); border:1px solid var(--border-color); overflow:hidden; }\n.header { padding:20px 16px; background:linear-gradient(135deg,var(--color-primary),var(--color-primary-light)); text-align:center; }\n.header-title { font-size:18px; font-weight:700; color:#fff; margin-bottom:4px; }\n.header-desc { font-size:12px; color:rgba(255,255,255,.8); }\n.tip-alert { background:linear-gradient(135deg,rgba(255,193,7,.12),rgba(255,152,0,.08)); border:1px solid rgba(255,193,7,.25); border-radius:16px; padding:14px 16px; margin:16px; font-size:13px; color:var(--text-secondary); line-height:1.6; }\n.tip-alert .highlight-text { color:#ea580c; font-weight:700; }\n.setting-item { display:flex; align-items:center; justify-content:space-between; padding:16px; border-bottom:1px solid var(--border-color); }\n.setting-item:last-child { border-bottom:none; }\n.setting-title { font-size:15px; font-weight:700; margin-bottom:4px; }\n.setting-desc { font-size:12px; color:var(--text-secondary); line-height:1.4; }\n.switch { position:relative; display:inline-block; width:48px; height:28px; flex-shrink:0; }\n.switch input { opacity:0; width:0; height:0; }\n.slider { position:absolute; cursor:pointer; top:0; left:0; right:0; bottom:0; background-color:#ccc; transition:.3s; border-radius:28px; }\n.slider:before { position:absolute; content:\"\"; height:22px; width:22px; left:3px; bottom:3px; background-color:#fff; transition:.3s; border-radius:50%; box-shadow:0 2px 4px rgba(0,0,0,.2); }\ninput:checked + .slider { background:linear-gradient(135deg,var(--color-primary),var(--color-primary-light)); }\ninput:checked + .slider:before { transform:translateX(20px); }\n.select-wrapper select { appearance:none; -webkit-appearance:none; padding:8px 32px 8px 12px; font-size:13px; font-weight:600; color:var(--text-primary); background:var(--bg-elevated); border:1px solid var(--border-color); border-radius:8px; cursor:pointer; min-width:110px; }\n</style>\n</head>\n<body>\n<div class=\"container\">\n <div class=\"main-card\">\n <div class=\"header\">\n <div class=\"header-title\">🍅 番茄聚合 · 书源设置</div>\n <div class=\"header-desc\">数据服务由 fanqie-go 提供</div>\n </div>\n <div class=\"tip-alert\">\n <p>设置完成后请点击右上角的<span class=\"highlight-text\">√</span>应用设置</p>\n </div>\n <div class=\"setting-item\">\n <div>\n <div class=\"setting-title\">搜索模式</div>\n <div class=\"setting-desc\">切换后搜索对应类型,也可以在搜索时使用关键词:t:书名 搜听书,d:书名 搜短剧,m:书名 搜漫画</div>\n </div>\n <div class=\"select-wrapper\">\n <select id=\"searchMode\">\n <option value=\"小说\">小说</option>\n <option value=\"听书\">听书</option>\n <option value=\"短剧\">短剧</option>\n <option value=\"漫画\">漫画</option>\n </select>\n </div>\n </div>\n <div class=\"setting-item\">\n <div>\n <div class=\"setting-title\">显示图片</div>\n <div class=\"setting-desc\">关闭后,文章内的图片将会清除</div>\n </div>\n <label class=\"switch\"><input type=\"checkbox\" id=\"showImageSwitch\" checked><span class=\"slider\"></span></label>\n </div>\n <div class=\"setting-item\">\n <div>\n <div class=\"setting-title\">完整简介</div>\n <div class=\"setting-desc\">关闭后,书籍详情页会精简简介内容</div>\n </div>\n <label class=\"switch\"><input type=\"checkbox\" id=\"fullDescSwitch\" checked><span class=\"slider\"></span></label>\n </div>\n <div id=\"hiddenSettings\" style=\"display:none\">\n <span id=\"searchModeValue\">${(data && data.搜索模式) || '小说'}</span>\n <span id=\"showImageSwitchValue\">${(data && data.显示图片) || 'true'}</span>\n <span id=\"fullDescSwitchValue\">${(data && data.完整简介) || 'true'}</span>\n </div>\n </div>\n</div>\n<script>\nlet settings = {\n '搜索模式': '${(data && data.搜索模式) || '小说'}',\n '显示图片': '${(data && data.显示图片) || 'true'}',\n '完整简介': '${(data && data.完整简介) || 'true'}'\n};\ndocument.getElementById('searchMode').addEventListener('change', e => { settings['搜索模式'] = e.target.value; update(); });\ndocument.getElementById('showImageSwitch').addEventListener('change', e => { settings['显示图片'] = e.target.checked ? 'true' : 'false'; update(); });\ndocument.getElementById('fullDescSwitch').addEventListener('change', e => { settings['完整简介'] = e.target.checked ? 'true' : 'false'; update(); });\ndocument.getElementById('searchMode').value = settings['搜索模式'];\ndocument.getElementById('showImageSwitch').checked = settings['显示图片'] != 'false';\ndocument.getElementById('fullDescSwitch').checked = settings['完整简介'] != 'false';\nfunction update() {\n document.getElementById('searchModeValue').textContent = settings['搜索模式'];\n document.getElementById('showImageSwitchValue').textContent = settings['显示图片'];\n document.getElementById('fullDescSwitchValue').textContent = settings['完整简介'];\n}\n</script>\n</body>\n</html>`;\n let body;\n try {\n body = java.startBrowserAwait(`data:text/html;base64,${java.base64Encode(html)}`, '番茄聚合书源设置', false).body();\n } catch (e) {\n java.longToast(\"\\n请点击页面右上角√才能设置成功哦~\");\n return;\n }\n let settings = {};\n try {\n const getSpanText = (spanId) => {\n const regex = new RegExp('id=\"' + spanId + '\"\\s*>\\s*([^<]*?)\\s*<\\/span>');\n const match = body.match(regex);\n return match ? match[1].trim() : \"\";\n };\n settings = {\n '搜索模式': getSpanText('searchModeValue') || '小说',\n '显示图片': getSpanText('showImageSwitchValue') || 'true',\n '完整简介': getSpanText('fullDescSwitchValue') || 'true',\n };\n const settingsText = Object.keys(settings).map(key => `${key}: ${settings[key]}`).join('\\n');\n java.longToast(\"\\n\" + settingsText);\n } catch (e) {}\n this.setVariable(\"更多设置\", settings, false);\n}\n\n// ============ 段评设置页 ============\nfunction getSvgSettings() {\n const { java } = this;\n const islyc = this.checkEnv();\n if (islyc == \"苹果\") {\n java.longToast(\"\\n当前软件不支持设置段评样式\");\n return;\n }\n let data = this.getVariable('段评设置');\n let html = `<!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,maximum-scale=1,user-scalable=no\">\n<title>番茄聚合 - 段评设置</title>\n<style>\n* { margin:0; padding:0; box-sizing:border-box; -webkit-tap-highlight-color:transparent; }\n:root {\n --color-primary:#2D9D78; --color-primary-light:#3DB893;\n --bg-card:rgba(255,255,255,.98); --bg-elevated:#fafbfc;\n --text-primary:#1a1d23; --text-secondary:#6b7280;\n --border-color:rgba(0,0,0,.08);\n}\nhtml,body { width:100%; min-height:100vh; font-family:-apple-system,BlinkMacSystemFont,sans-serif; font-size:16px; color:var(--text-primary); background:linear-gradient(135deg,#eef2ff,#f5f7ff); }\n.container { padding:16px; max-width:600px; margin:0 auto; }\n.main-card { background:var(--bg-card); border-radius:20px; box-shadow:0 4px 24px rgba(0,0,0,.1); border:1px solid var(--border-color); overflow:hidden; }\n.header { padding:20px 16px; background:linear-gradient(135deg,var(--color-primary),var(--color-primary-light)); text-align:center; }\n.header-title { font-size:18px; font-weight:700; color:#fff; margin-bottom:4px; }\n.header-desc { font-size:12px; color:rgba(255,255,255,.8); }\n.tip-alert { background:linear-gradient(135deg,rgba(255,193,7,.12),rgba(255,152,0,.08)); border:1px solid rgba(255,193,7,.25); border-radius:16px; padding:14px 16px; margin:16px; font-size:13px; color:var(--text-secondary); line-height:1.6; }\n.tip-alert .highlight-text { color:#ea580c; font-weight:700; }\n.setting-item { display:flex; align-items:center; justify-content:space-between; padding:16px; border-bottom:1px solid var(--border-color); }\n.setting-title { font-size:15px; font-weight:700; margin-bottom:4px; }\n.setting-desc { font-size:12px; color:var(--text-secondary); line-height:1.4; }\n.switch { position:relative; display:inline-block; width:48px; height:28px; flex-shrink:0; }\n.switch input { opacity:0; width:0; height:0; }\n.slider { position:absolute; cursor:pointer; top:0; left:0; right:0; bottom:0; background-color:#ccc; transition:.3s; border-radius:28px; }\n.slider:before { position:absolute; content:\"\"; height:22px; width:22px; left:3px; bottom:3px; background-color:#fff; transition:.3s; border-radius:50%; box-shadow:0 2px 4px rgba(0,0,0,.2); }\ninput:checked + .slider { background:linear-gradient(135deg,var(--color-primary),var(--color-primary-light)); }\ninput:checked + .slider:before { transform:translateX(20px); }\n.select-wrapper select { appearance:none; -webkit-appearance:none; padding:8px 32px 8px 12px; font-size:13px; font-weight:600; color:var(--text-primary); background:var(--bg-elevated); border:1px solid var(--border-color); border-radius:8px; cursor:pointer; min-width:130px; }\n.color-picker-wrapper { display:flex; align-items:center; gap:8px; flex-shrink:0; }\n.color-input { width:40px; height:40px; border:none; border-radius:8px; cursor:pointer; background:transparent; padding:2px; }\n.color-text-input { width:100px; padding:8px 10px; font-size:12px; font-weight:600; color:var(--text-primary); background:var(--bg-elevated); border:1px solid var(--border-color); border-radius:6px; }\n.transparent-btn { padding:8px 12px; font-size:11px; font-weight:600; color:var(--color-primary); background:rgba(45,157,120,.1); border:1px solid var(--color-primary); border-radius:6px; cursor:pointer; }\n.preview-section { padding:20px; background:var(--bg-elevated); border-top:1px solid var(--border-color); }\n.preview-title { font-size:14px; font-weight:700; margin-bottom:16px; }\n.preview-container { display:flex; flex-wrap:wrap; gap:16px; justify-content:center; align-items:center; min-height:120px; }\n.preview-item { display:flex; flex-direction:column; align-items:center; gap:8px; padding:12px; background:var(--bg-card); border-radius:12px; border:2px solid transparent; cursor:pointer; }\n.preview-item.selected { border-color:var(--color-primary); background:rgba(45,157,120,.05); }\n.preview-item svg { width:60px; height:54px; }\n.preview-label { font-size:11px; color:var(--text-secondary); }\n.preview-item.selected .preview-label { color:var(--color-primary); font-weight:600; }\n</style>\n</head>\n<body>\n<div class=\"container\">\n <div class=\"main-card\">\n <div class=\"header\">\n <div class=\"header-title\">🗨 番茄聚合 · 段评设置</div>\n <div class=\"header-desc\">点击正文段尾气泡查看段落评论</div>\n </div>\n <div class=\"tip-alert\">\n <p>设置完成后请点击右上角的<span class=\"highlight-text\">√</span>应用设置</p>\n </div>\n <div class=\"setting-item\">\n <div>\n <div class=\"setting-title\">段评</div>\n <div class=\"setting-desc\">阅读正文时在段落末尾显示评论气泡</div>\n </div>\n <label class=\"switch\"><input type=\"checkbox\" id=\"duanpingSwitch\" checked><span class=\"slider\"></span></label>\n </div>\n <div class=\"setting-item\">\n <div>\n <div class=\"setting-title\">气泡增大</div>\n <div class=\"setting-desc\">开启后,气泡显示会加大一些</div>\n </div>\n <label class=\"switch\"><input type=\"checkbox\" id=\"bubbleLargeSwitch\"><span class=\"slider\"></span></label>\n </div>\n <div class=\"setting-item\">\n <div>\n <div class=\"setting-title\">边框颜色</div>\n <div class=\"setting-desc\">设置段评边框颜色</div>\n </div>\n <div class=\"color-picker-wrapper\">\n <input type=\"color\" id=\"borderColor\" value=\"#A7A7A7\" class=\"color-input\">\n <input type=\"text\" id=\"borderColorText\" value=\"#A7A7A7\" class=\"color-text-input\">\n </div>\n </div>\n <div class=\"setting-item\">\n <div>\n <div class=\"setting-title\">字体颜色</div>\n <div class=\"setting-desc\">设置段评字体颜色</div>\n </div>\n <div class=\"color-picker-wrapper\">\n <input type=\"color\" id=\"fontColor\" value=\"#A7A7A7\" class=\"color-input\">\n <input type=\"text\" id=\"fontColorText\" value=\"#A7A7A7\" class=\"color-text-input\">\n </div>\n </div>\n <div class=\"setting-item\">\n <div>\n <div class=\"setting-title\">填充颜色</div>\n <div class=\"setting-desc\">设置段评背景填充颜色</div>\n </div>\n <div class=\"color-picker-wrapper\">\n <input type=\"color\" id=\"fillColor\" value=\"#FFFFFF\" class=\"color-input\">\n <input type=\"text\" id=\"fillColorText\" value=\"transparent\" class=\"color-text-input\">\n <button id=\"fillColorTransparent\" class=\"transparent-btn\">透明</button>\n </div>\n </div>\n <div class=\"preview-section\">\n <div class=\"preview-title\">实时预览(点击选择样式)</div>\n <div id=\"previewContainer\" class=\"preview-container\"></div>\n </div>\n <div id=\"hiddenSettings\" style=\"display:none\">\n <span id=\"duanpingSwitchValue\">${(data && data.段评开关) || 'true'}</span>\n <span id=\"bubbleLargeSwitchValue\">${(data && data.气泡增大) || 'false'}</span>\n <span id=\"duanpingStyleValue\">${(data && data.段评样式) || '0'}</span>\n <span id=\"borderColorValue\">${(data && data.段评边框颜色) || '#A7A7A7'}</span>\n <span id=\"fontColorValue\">${(data && data.段评字体颜色) || '#A7A7A7'}</span>\n <span id=\"fillColorValue\">${(data && data.段评填充颜色) || 'transparent'}</span>\n </div>\n </div>\n</div>\n<script>\nconst SVG_TEMPLATES = ${JSON.stringify(svgs)};\nconst STYLE_NAMES = ['起点样式', '番茄样式', '精致样式', '对话样式', '方框样式', '圆圈样式'];\nlet settings = {\n '段评开关': '${(data && data.段评开关) || 'true'}',\n '气泡增大': '${(data && data.气泡增大) || 'false'}',\n '段评样式': '${(data && data.段评样式) || '0'}',\n '段评边框颜色': '${(data && data.段评边框颜色) || '#A7A7A7'}',\n '段评字体颜色': '${(data && data.段评字体颜色) || '#A7A7A7'}',\n '段评填充颜色': '${(data && data.段评填充颜色) || 'transparent'}'\n};\nfunction update() {\n document.getElementById('duanpingSwitchValue').textContent = settings['段评开关'];\n document.getElementById('bubbleLargeSwitchValue').textContent = settings['气泡增大'];\n document.getElementById('duanpingStyleValue').textContent = settings['段评样式'];\n document.getElementById('borderColorValue').textContent = settings['段评边框颜色'];\n document.getElementById('fontColorValue').textContent = settings['段评字体颜色'];\n document.getElementById('fillColorValue').textContent = settings['段评填充颜色'];\n}\nfunction renderPreview() {\n const container = document.getElementById('previewContainer');\n container.innerHTML = '';\n SVG_TEMPLATES.forEach((tpl, index) => {\n const content = tpl.split('$color').join(settings['段评边框颜色'])\n .split('$fontcolor').join(settings['段评字体颜色'])\n .split('$tccolor').join(settings['段评填充颜色'])\n .split('$displayText').join('99+');\n const item = document.createElement('div');\n item.className = 'preview-item ' + (settings['段评样式'] === String(index) ? 'selected' : '');\n item.innerHTML = content + '<div class=\"preview-label\">' + STYLE_NAMES[index] + '</div>';\n item.addEventListener('click', () => {\n settings['段评样式'] = String(index);\n update(); renderPreview();\n });\n container.appendChild(item);\n });\n}\ndocument.getElementById('duanpingSwitch').addEventListener('change', e => { settings['段评开关'] = e.target.checked ? 'true' : 'false'; update(); });\ndocument.getElementById('bubbleLargeSwitch').addEventListener('change', e => { settings['气泡增大'] = e.target.checked ? 'true' : 'false'; update(); });\ndocument.getElementById('borderColor').addEventListener('input', e => { settings['段评边框颜色'] = e.target.value; document.getElementById('borderColorText').value = e.target.value; update(); renderPreview(); });\ndocument.getElementById('borderColorText').addEventListener('input', e => { if (/^#[0-9A-Fa-f]{6}$/.test(e.target.value)) { settings['段评边框颜色'] = e.target.value; document.getElementById('borderColor').value = e.target.value; update(); renderPreview(); } });\ndocument.getElementById('fontColor').addEventListener('input', e => { settings['段评字体颜色'] = e.target.value; document.getElementById('fontColorText').value = e.target.value; update(); renderPreview(); });\ndocument.getElementById('fontColorText').addEventListener('input', e => { if (/^#[0-9A-Fa-f]{6}$/.test(e.target.value)) { settings['段评字体颜色'] = e.target.value; document.getElementById('fontColor').value = e.target.value; update(); renderPreview(); } });\ndocument.getElementById('fillColor').addEventListener('input', e => { settings['段评填充颜色'] = e.target.value; document.getElementById('fillColorText').value = e.target.value; update(); renderPreview(); });\ndocument.getElementById('fillColorText').addEventListener('input', e => { if (e.target.value.toLowerCase() == 'transparent' || /^#[0-9A-Fa-f]{6}$/.test(e.target.value)) { settings['段评填充颜色'] = e.target.value; update(); renderPreview(); } });\ndocument.getElementById('fillColorTransparent').addEventListener('click', () => { settings['段评填充颜色'] = 'transparent'; document.getElementById('fillColorText').value = 'transparent'; document.getElementById('fillColor').value = '#FFFFFF'; update(); renderPreview(); });\ndocument.getElementById('duanpingSwitch').checked = settings['段评开关'] != 'false';\ndocument.getElementById('bubbleLargeSwitch').checked = settings['气泡增大'] == 'true';\nrenderPreview();\n</script>\n</body>\n</html>`;\n let body;\n try {\n body = java.startBrowserAwait(`data:text/html;base64,${java.base64Encode(html)}`, '番茄聚合段评设置', false).body();\n } catch (e) {\n if (islyc == \"安卓\") {\n java.longToast(\"\\n当前软件版本过低,请更新软件,或者使用底部备用设置方案~\");\n } else {\n java.longToast(\"\\n请点击页面右上角√才能设置成功哦~\");\n }\n return;\n }\n let settings = {};\n try {\n const getSpanText = (spanId) => {\n const regex = new RegExp('id=\"' + spanId + '\"\\s*>\\s*([^<]*?)\\s*<\\/span>');\n const match = body.match(regex);\n return match ? match[1].trim() : \"\";\n };\n settings = {\n '段评开关': getSpanText('duanpingSwitchValue') || 'true',\n '气泡增大': getSpanText('bubbleLargeSwitchValue') || 'false',\n '段评样式': getSpanText('duanpingStyleValue') || '0',\n '段评边框颜色': getSpanText('borderColorValue') || '#A7A7A7',\n '段评字体颜色': getSpanText('fontColorValue') || '#A7A7A7',\n '段评填充颜色': getSpanText('fillColorValue') || 'transparent',\n };\n const settingsText = Object.keys(settings).map(key => `${key}: ${settings[key]}`).join('\\n');\n java.longToast(\"\\n\" + settingsText);\n } catch (e) {\n settings = {\n '段评开关': 'true',\n '气泡增大': 'false',\n '段评样式': '0',\n '段评边框颜色': '#A7A7A7',\n '段评字体颜色': '#A7A7A7',\n '段评填充颜色': 'transparent'\n };\n }\n this.setVariable(\"段评设置\", settings, false);\n}\n",
|
||
"loginUi": "[\n {\n \"name\": \"⚙️ 书源设置\",\n \"type\": \"button\",\n \"action\": \"getHtmlSettings()\",\n \"style\": { \"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 0.4 }\n }, {\n \"name\": \"🗨 段评设置\",\n \"type\": \"button\",\n \"action\": \"getSvgSettings()\",\n \"style\": { \"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 0.4 }\n }, {\n \"name\": \"===下方为备用设置方案===\",\n \"type\": \"button\",\n \"action\": \"\",\n \"style\": { \"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 1 }\n }, {\n \"name\": \"📖搜索小说\",\n \"type\": \"button\",\n \"action\": \"ste('小说')\",\n \"style\": { \"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 0.4 }\n }, {\n \"name\": \"🎧搜索听书\",\n \"type\": \"button\",\n \"action\": \"ste('听书')\",\n \"style\": { \"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 0.4 }\n }, {\n \"name\": \"🎥搜索短剧\",\n \"type\": \"button\",\n \"action\": \"ste('短剧')\",\n \"style\": { \"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 0.4 }\n }, {\n \"name\": \"🎨搜索漫画\",\n \"type\": \"button\",\n \"action\": \"ste('漫画')\",\n \"style\": { \"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 0.4 }\n }, {\n \"name\": \"段评开关\",\n \"type\": \"button\",\n \"action\": \"ste('段评')\",\n \"style\": { \"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 0.25 }\n }, {\n \"name\": \"图片开关\",\n \"type\": \"button\",\n \"action\": \"ste('图片')\",\n \"style\": { \"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 0.25 }\n }, {\n \"name\": \"简介开关\",\n \"type\": \"button\",\n \"action\": \"ste('简介')\",\n \"style\": { \"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 0.25 }\n }, {\n \"name\": \"⛔‼️ 清空还原设置\",\n \"type\": \"button\",\n \"action\": \"deleteVariable()\",\n \"style\": { \"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 1 }\n }\n]",
|
||
"searchUrl": "<js>\nlet moreSettings = getVariable('更多设置');\nlet tab = moreSettings && moreSettings['搜索模式'] || '小说';\nvar k = String(key);\nlet m = k.match(/^[xtmdXTMD][::]\\s*/);\nif (m) {\n tab = { x: '小说', t: '听书', m: '漫画', d: '短剧' }[m[0][0].toLowerCase()];\n k = k.slice(m[0].length);\n}\nlet tabType = { 小说: 3, 听书: 2, 漫画: 8, 短剧: 11 }[tab] || 3;\n`/search?query=${encodeURIComponent(k)}&offset=${(page - 1) * 10}&tab_type=${tabType}`;\n</js>",
|
||
"exploreUrl": "<js>\nvar base = String(source.getKey()).replace(/\\/$/, '');\nvar tab = getVariable('发现页类型') || '小说';\nvar channel = getVariable('频道') || '男频';\nvar wordNumMap = { '不限': '0', '30万字以内': '4', '100万字以内': '1', '30万-100万': '5', '100万-300万': '2', '300万字以上': '3', '100万字以上': '9' };\nvar updateMap = { '不限': '2', '完结': '0', '连载中': '1', '今日更新': '3' };\nvar sortMap = { '综合': '0', '热门': '24', '最新': '76', '评分': '12' };\nvar wordNum = getVariable('字数') || '不限';\nvar update = getVariable('更新') || '不限';\nvar sort = getVariable('排序') || '综合';\nvar q = `&word_number=${wordNumMap[wordNum] || '0'}&book_status=${updateMap[update] || '2'}&sort_by=${sortMap[sort] || '0'}`;\nvar tabCode = { 小说: 'n', 听书: 't', 漫画: 'm', 短剧: 'd' }[tab] || 'n';\n\nvar list = [];\nlist.push(createFilter('类型', ['小说', '听书', '短剧', '漫画'], tab, '发现页类型', 0.4));\nlist.push(createFilter('频道', ['男频', '女频'], channel, '频道', 0.4));\nlist.push(createText('搜索关键词', \"setVariable('搜索关键词',(infoMap['搜索关键词'] || (infoMap.get && infoMap.get('搜索关键词'))))\", getVariable('搜索关键词') || '', 0.5, \"'搜索书名或作者'\"));\nlist.push(createButton('🔍搜索', 'exploreSearch()', 0.3));\nlist.push({ title: '⚙️书源设置', type: 'button', action: 'getHtmlSettings()', style: { layout_flexGrow: 1, layout_flexBasisPercent: 0.25 } });\nlist.push({ title: '🗨段评设置', type: 'button', action: 'getSvgSettings()', style: { layout_flexGrow: 1, layout_flexBasisPercent: 0.25 } });\nlist.push(createFilter('字数', Object.keys(wordNumMap), wordNum, '字数', 0.4));\nlist.push(createFilter('更新', Object.keys(updateMap), update, '更新', 0.4));\nlist.push(createFilter('排序', Object.keys(sortMap), sort, '排序', 0.1));\n\nif (tab == '短剧') {\n list.push({ title: '短剧暂无分类,请使用上方搜索 🔍', url: '', style: { layout_flexGrow: 1, layout_flexBasisPercent: 1 } });\n} else {\n var ctab = tab == '小说' ? (channel == '女频' ? 0 : 1) : (tab == '听书' ? 3 : 5);\n try {\n var res = JSON.parse(java.ajax(base + '/category-front?tab=' + ctab));\n var cells = ((res.data || {}).category_tab_data || {}).cell_data || [];\n cells.forEach(function (cell) {\n (cell.atom_data || []).forEach(function (a) {\n var c = a.category_data;\n if (c && c.category_id != null) {\n list.push({\n title: c.name,\n url: base + '/category-landing?category_id=' + c.category_id + '&offset={{(page-1)*20}}' + q + '&fqtab=' + tabCode,\n style: { layout_flexGrow: 1, layout_flexBasisPercent: 0.25 }\n });\n }\n });\n });\n } catch (e) {\n java.toast('发现页分类获取失败');\n }\n}\nJSON.stringify(list);\n</js>",
|
||
"ruleSearch": {
|
||
"bookList": "<js>\nvar d = JSON.parse(result);\nvar isVideo = /tab_type=11/.test(baseUrl);\nvar arr = [];\n(d.data || []).forEach(function (b) {\n var list = isVideo ? (b.video_data || []) : (b.book_data || []);\n list.forEach(function (x) {\n if (isVideo) {\n arr.push({\n book_id: x.series_id,\n book_name: x.title,\n author: '',\n abstract: x.rec_text || '',\n thumb_url: x.horiz_cover || x.cover,\n category: '短剧',\n creation_status: '1',\n score: x.score,\n word_number: String(x.episode_cnt || '')\n });\n } else {\n arr.push(x);\n }\n });\n});\narr;\n</js>",
|
||
"name": "$.book_name",
|
||
"author": "$.author",
|
||
"kind": "{{$.creation_status=='0'?'完结':'连载'}},{{$.category}},{{$.score}}分",
|
||
"wordCount": "$.word_number##^0+$",
|
||
"intro": "$.abstract",
|
||
"coverUrl": "$.thumb_url",
|
||
"bookUrl": "<js>\nvar t = /tab_type=11/.test(baseUrl) ? 'd' : /tab_type=2/.test(baseUrl) ? 't' : /tab_type=8/.test(baseUrl) ? 'm' : 'n';\n`/detail?book_id=${result.book_id}&fqtab=${t}`;\n</js>",
|
||
"checkKeyWord": "斗罗大陆"
|
||
},
|
||
"ruleExplore": {
|
||
"bookList": "$.data.book_info[*]",
|
||
"name": "$.book_name",
|
||
"author": "$.author",
|
||
"kind": "{{$.category}},{{$.score}}分",
|
||
"wordCount": "$.word_number##^0+$",
|
||
"intro": "$.abstract",
|
||
"coverUrl": "$.thumb_url",
|
||
"bookUrl": "<js>\nvar m = String(baseUrl).match(/fqtab=([ntmd])/);\n`/detail?book_id=${result.book_id}&fqtab=${m ? m[1] : 'n'}`;\n</js>"
|
||
},
|
||
"ruleBookInfo": {
|
||
"init": "$.data",
|
||
"name": "$.book_name",
|
||
"author": "$.author",
|
||
"kind": "{{$.creation_status=='0'?'完结':'连载'}},{{$.category}}",
|
||
"wordCount": "$.word_number##^0+$",
|
||
"lastChapter": "$.last_chapter_title",
|
||
"coverUrl": "$.thumb_url",
|
||
"intro": "<js>\nconst { book_id, last_chapter_title, last_chapter_update_time, word_number, score, abstract, read_count, category } = result;\nlet m0 = String(baseUrl).match(/fqtab=([ntmd])/);\nconst tab = { n: '小说', t: '听书', m: '漫画', d: '短剧' }[(m0 ? m0[1] : 'n')];\nlet moreSettings = getVariable('更多设置');\nconst lightDivider = \"❇️───────❇️───────❇️\";\nconst heavyDivider = \"‎\\n‎\";\nconst isValid = (v) => String(v).length > 1;\nlet ctitle = book.durChapterTitle || '未开始';\nconst two = (n) => ('0' + n).slice(-2);\nconst fmtTime = (t) => {\n let n = parseInt(t);\n if (!n || n <= 0) return '';\n let d = new Date(n * 1000);\n return `${d.getFullYear()}-${two(d.getMonth() + 1)}-${two(d.getDate())}`;\n};\nconst formatAbstract = (text) => String(text).split(\"\\n\").map(line => ` ${line}`).join(\"\\n\");\nconst buildBasicInfo = (items) => {\n let info = \"\";\n for (let [value, prefix, icon] of items) {\n if (isValid(value)) info += ` ${icon} ${prefix} ${value}\\n`;\n }\n return info;\n};\nlet info = ` 🏷 数据来源:番茄小说(fanqie-go)\\n 🔄 当前模式:${tab}\\n 📖 阅读至:${ctitle}\\n`;\nconst fullBasicInfo = buildBasicInfo([\n [category, \"书籍分类:\", \"🌈\"],\n [last_chapter_title, \"最新章节:\", \"📚\"],\n [fmtTime(last_chapter_update_time), \"更新时间:\", \"⏳\"],\n [word_number, \"书籍字数:\", \"📝\"],\n [score, \"书籍评分:\", \"⭐\"],\n [read_count, \"今阅读人数:\", \"📃\"]\n]);\nif (fullBasicInfo) info += `${lightDivider}\\n${fullBasicInfo}`;\nif (isValid(abstract)) info += `${heavyDivider}\\n 📖 书籍简介:\\n${formatAbstract(abstract)}\\n`;\ninfo += `${lightDivider}\\n${heavyDivider}\\n 数据更新于 ${new Date().toLocaleString()}\\n`;\nconst abstractMode = moreSettings && moreSettings['完整简介'] || 'true';\nif (abstractMode == \"false\") {\n const simplifiedBasicInfo = buildBasicInfo([\n [last_chapter_title, \"最新章节:\", \"📚\"],\n [fmtTime(last_chapter_update_time), \"更新时间:\", \"⏳\"],\n [word_number, \"书籍字数:\", \"📝\"],\n [score, \"书籍评分:\", \"⭐\"]\n ]);\n info = `\\n‎${isValid(abstract) ? `📖 书籍简介:\\n${formatAbstract(abstract)}\\n${heavyDivider}` : ''}🏷 数据来源:番茄小说\\n${simplifiedBasicInfo}`;\n} else {\n info = info.split(\"\\n\").map(line => line.replace(/^ {4}/, \"\")).join(\"\\n\");\n}\ninfo;\n</js>",
|
||
"tocUrl": "<js>\nvar m = String(baseUrl).match(/fqtab=([ntmd])/);\n`/catalog?book_id=${result.book_id}&fqtab=${m ? m[1] : 'n'}`;\n</js>",
|
||
"canReName": "1"
|
||
},
|
||
"ruleToc": {
|
||
"chapterList": "<js>\n// 类型随书切换(照光遇 typeMap:安卓用 default,苹果端用其类型体系)\nconst typeMap = {\n \"小说\": { \"苹果\": 0, \"default\": 8 },\n \"听书\": { \"苹果\": 1, \"default\": 32 },\n \"漫画\": { \"苹果\": 2, \"default\": 64 },\n \"短剧\": { \"苹果\": 3, \"default\": 8 }\n};\nconst device_type = checkEnv();\nvar tabName = { n: '小说', t: '听书', m: '漫画', d: '短剧' }[fqTab(baseUrl, 'n')];\nvar currentType = typeMap[tabName] || typeMap[\"小说\"];\nif (device_type == '苹果') {\n book.type = currentType[device_type];\n} else {\n book.type = currentType.default;\n}\ntry {\n book.imageStyle = checkEnv() == '改版' ? 'FULL' : 'TEXT';\n} catch (e) {}\nresult;\n</js>$.data.item_data_list[*]",
|
||
"chapterName": "$.title",
|
||
"chapterUrl": "<js>\nvar t = fqTab(baseUrl, 'n');\nvar id = result.item_id;\nvar u = {\n n: `/content?item_id=${id}&text_mode=html&review=keep&image_mode=keep&fqtab=n`,\n t: `/audio?item_ids=${id}&fqtab=t`,\n m: `/manga?item_ids=${id}&output=proxy&fqtab=m`,\n d: `/video?item_ids=${id}&fqtab=d`\n}[t];\nu;\n</js>"
|
||
},
|
||
"ruleContent": {
|
||
"content": "<js>\nvar t = fqTab(baseUrl, 'n');\nlet moreSettings = getVariable('更多设置');\nlet dpSettings = getVariable('段评设置');\nlet islyc = checkEnv();\n\nif (t == 't') {\n // 听书:直接返回音频地址\n JSON.parse(result).data[0].main_url;\n} else if (t == 'm') {\n // 漫画:拼装图片\n var d = JSON.parse(result);\n var org = String(baseUrl).match(/^https?:\\/\\/[^\\/]+/)[0];\n var imgs = (d.data || {}).images || [];\n var out = [];\n for (var i = 0; i < imgs.length; i++) {\n out.push('<img src=\"' + org + imgs[i].data + '\">');\n }\n out.join('\\n');\n} else if (t == 'd') {\n // 短剧:选最高清晰度,当前章节自动唤起播放\n var d = JSON.parse(result).data || {};\n var vl = d.video_list || {};\n var list = [];\n for (var k in vl) { list.push(vl[k]); }\n list.sort(function (a, b) { return (b.vheight || 0) - (a.vheight || 0); });\n var org = String(baseUrl).match(/^https?:\\/\\/[^\\/]+/)[0];\n var url = list.length ? String(list[0].main_url) : '';\n if (url && url.indexOf('http') != 0) { url = org + url; }\n if (url && book.durChapterIndex == chapter.index) {\n java.startBrowser(url, chapter.title);\n }\n '▶️ 本集已唤起播放(菜单-刷新可重新打开):\\n' + url;\n} else {\n // 小说:正文 + 段评气泡\n var d = JSON.parse(result);\n var content = String((d.data || {}).content || '');\n let show_img = moreSettings && moreSettings['显示图片'] || 'true';\n if (show_img == 'false') {\n content = removeAllImgTags(content);\n }\n let para = dpSettings && dpSettings['段评开关'] || 'true';\n if (para == 'true' && content.indexOf('<comment') != -1) {\n content = islyc == '苹果' ? paraForiOS(content, '番茄') : paraForAndroid(content, '番茄');\n }\n content;\n}\n</js>",
|
||
"title": "$.data.title"
|
||
}
|
||
}
|
||
] |