📖 淘小说API
https://tybook.taoyuewenhua.net
laizhiyu (6509)1天前
//淘小说APP接口API源
{
"bookSourceComment": "\/\/淘小说APP接口API源",
"bookSourceGroup": "APP接口",
"bookSourceName": "📖 淘小说API",
"bookSourceType": 0,
"bookSourceUrl": "https:\/\/tybook.taoyuewenhua.net",
"bookUrlPattern": "https?:\/\/(?:tybook|scdn|app)\\.taoyuewenhua\\.net\/(?:tf\/book|app\/chapters)\\?.*|https?:\/\/app\\.taoyuewenhua\\.net\/tybook\/.*|ttbook:\/\/m\\.taoyuewenhua\\.com\/(?:push|game|homepage)\/.*",
"concurrentRate": "1\/1500",
"customButton": false,
"customOrder": 2,
"enabled": true,
"enabledCookieJar": true,
"enabledExplore": true,
"eventListener": false,
"exploreUrl": "@js:\n(function(){\n var CFG={\"男生\":{\"ctype\":\"1\",\"freeType\":\"1\",\"modules\":[[\"推荐榜\",-1000006],[\"好看到停不下来\",61]],\"ranks\":[[\"推荐榜\",100],[\"完本榜\",110],[\"人气榜\",10],[\"热搜榜\",80],[\"收藏榜\",20],[\"阅读榜\",30]],\"cats\":[[\"都市\",105000],[\"玄幻\",101000],[\"历史\",106000],[\"军事\",107000],[\"奇闻怪谈\",108000],[\"武侠\",109000],[\"科幻\",110000],[\"游戏\",111000],[\"仙侠\",102000],[\"悬疑\",112000],[\"轻小说\",113000],[\"都市修真\",114000]],\"tags\":[\"末世\",\"系统\",\"穿越\",\"重生\",\"无敌\",\"签到\",\"爽文\",\"打脸\",\"赘婿\",\"战神\",\"神医\",\"奶爸\",\"脑洞\",\"网游\",\"同人\",\"无限流\",\"种田\",\"都市\"],\"tagFrom\":\"5\"},\"女生\":{\"ctype\":\"2\",\"freeType\":\"2\",\"modules\":[[\"推荐榜\",-1000005],[\"恋爱神作\",69]],\"ranks\":[[\"推荐榜\",100],[\"完本榜\",110],[\"人气榜\",10],[\"热搜榜\",80],[\"收藏榜\",20],[\"阅读榜\",30]],\"cats\":[[\"现代言情\",204000],[\"古代言情\",203000],[\"幻想言情\",205000],[\"浪漫青春\",206000],[\"悬疑推理\",208000],[\"短篇\",210000],[\"豪门总裁\",207000],[\"穿越时空\",209000],[\"宫闱宅斗\",211000]],\"tags\":[\"重生\",\"穿越\",\"年代\",\"娱乐圈\",\"系统\",\"爽文\",\"种田\"],\"tagFrom\":\"5\"},\"完结\":{\"ctype\":\"1\",\"freeType\":\"1\",\"modules\":[[\"精品完结\",32],[\"都市爽文\",41],[\"玄幻奇文\",42],[\"最新完结\",-1000016]],\"ranks\":[[\"完本榜\",110],[\"推荐榜\",100],[\"人气榜\",10],[\"收藏榜\",20],[\"阅读榜\",30]],\"cats\":[[\"完结·都市\",105000],[\"完结·玄幻\",101000],[\"完结·历史\",106000],[\"完结·仙侠\",102000],[\"完结·科幻\",110000],[\"完结·游戏\",111000]],\"tags\":[\"爽文\",\"系统\",\"重生\",\"无敌\",\"种田\",\"年代\",\"脑洞\",\"无限流\",\"末世\",\"穿越\"],\"tagFrom\":\"5\"},\"新书\":{\"ctype\":\"1\",\"freeType\":\"1\",\"modules\":[[\"精品新书\",37],[\"都市爽文\",48],[\"玄幻奇文\",49],[\"最新上架\",-1000004]],\"ranks\":[[\"推荐榜\",100],[\"人气榜\",10],[\"热搜榜\",80],[\"新书榜\",100]],\"cats\":[[\"新书·都市\",105000],[\"新书·玄幻\",101000],[\"新书·仙侠\",102000],[\"新书·科幻\",110000],[\"新书·历史\",106000],[\"新书·游戏\",111000]],\"tags\":[\"系统\",\"穿越\",\"重生\",\"脑洞\",\"签到\",\"都市\",\"玄幻\",\"爽文\",\"无敌\",\"末世\"],\"tagFrom\":\"5\"},\"阅文好书\":{\"ctype\":\"1\",\"freeType\":\"1\",\"modules\":[[\"热门精品\",26],[\"主编强推\",27],[\"名家神作\",29],[\"完结好书\",30],[\"新书上架\",28],[\"精品推荐\",31]],\"ranks\":[[\"推荐榜\",100],[\"完本榜\",110],[\"人气榜\",10],[\"阅读榜\",30]],\"cats\":[[\"名家·都市\",105000],[\"名家·玄幻\",101000],[\"名家·仙侠\",102000],[\"名家·历史\",106000],[\"名家·科幻\",110000],[\"名家·悬疑\",112000]],\"tags\":[\"热血\",\"玄幻\",\"仙侠\",\"都市\",\"系统\",\"重生\",\"穿越\",\"爽文\",\"种田\",\"脑洞\"],\"tagFrom\":\"5\"}};\n var NAMES=[\"男生\",\"女生\",\"完结\",\"新书\",\"阅文好书\"];\n \/\/ 选中频道:infoMap 优先(发现页 select 真机注入),java.get 兜底(无 infoMap 版本)\n var ch=\"\",hasIm=false;\n try{ if(typeof infoMap!==\"undefined\" && infoMap[\"频道\"]){ch=String(infoMap[\"频道\"]);hasIm=true;} }catch(e){}\n if(!hasIm){ try{ var v=String(java.get(\"txChannel\")||\"\"); if(v) ch=v; }catch(e){} }\n if(!CFG[ch]) ch=\"男生\";\n var c=CFG[ch], out=[];\n function head(t){\n out.push({title:t,url:\"\",type:\"button\",action:\"java.toast('\"+t+\"')\",\n style:{layout_flexBasisPercent:1,layout_wrapBefore:true,layout_justifySelf:\"flex_start\"}});\n }\n function row(t,path,extra){\n out.push({title:t,\n url:\"@js:\\nTX_explore(java, source, \"+JSON.stringify(path)+\", \"+JSON.stringify(extra)+\", page);\",\n type:\"url\",style:{layout_flexGrow:1,layout_flexBasisPercent:0.25}});\n }\n \/\/ ① 频道 select(5 选 1):infoMap 优先 → result 兜底 → 拿不到则保持原频道,\n \/\/ 再 java.refreshExplore() 清两级缓存并重绘(青橙 v7.1 成熟范式,LT\/E 均验证)\n out.push({title:\"频道\",type:\"select\",chars:NAMES,default:\"男生\",\n action:\"(function(){var v='';try{if(typeof infoMap!=='undefined'&&infoMap['频道'])v=String(infoMap['频道']);}catch(e){}if(!v){try{if(typeof result!=='undefined'&&result&&result.get)v=String(result.get('频道')||'');}catch(e2){}}if(v){java.put('txChannel',v);java.toast('✅ 已切换到频道:'+v);}else{java.toast('⚠ 未取到频道选项,保持原频道');}try{java.refreshExplore();}catch(e3){try{java.reUiView(true);}catch(e4){}}})()\",\n style:{layout_flexBasisPercent:1,layout_wrapBefore:true}});\n \/\/ ② 当前频道状态行(viewName 动态文本,返回纯文本不带外层引号)\n out.push({title:\"当前频道\",type:\"button\",action:\"\",\n viewName:\"'当前频道:'+(java.get('txChannel')||'男生')\",\n style:{layout_flexBasisPercent:1,layout_wrapBefore:true}});\n \/\/ ③ 频道真实模块(channel_books,带 page\/pageSize 由 Legado 递增,不写死页码上限)\n head(\"▌ \"+ch+\" · 频道精选\");\n for(var i=0;i<c.modules.length;i++){\n row(c.modules[i][0],\"\/book_mall\/channel_books\",\n {mcid:String(c.modules[i][1]),seed:\"0\",category:\"\",ext:\"\"});\n }\n \/\/ ④ 频道榜单(book_rank,brtype 来自 \/public\/mioptions 的 bookRankTabs)\n head(\"▌ \"+ch+\" · 排行榜\");\n for(var j=0;j<c.ranks.length;j++){\n row(c.ranks[j][0],\"\/book_mall\/book_rank\",\n {brtype:String(c.ranks[j][1]),ctype:c.ctype,status:\"0\"});\n }\n \/\/ ⑤ 频道分类(search_by_category,categoryId 来自旧源 + 真机抓包)\n head(\"▌ \"+ch+\" · 分类\");\n for(var k=0;k<c.cats.length;k++){\n row(c.cats[k][0],\"\/ty\/search_by_category\",\n {categoryId:String(c.cats[k][1]),freeType:c.freeType,from:\"13\",order:\"0\",status:\"0\"});\n }\n \/\/ ⑥ 频道热门标签(tags,from 用抓包实锤的 5)\n head(\"▌ \"+ch+\" · 热门标签\");\n for(var m=0;m<c.tags.length;m++){\n row(c.tags[m],\"\/ty\/search_by_category\",\n {freeType:c.freeType,from:c.tagFrom,order:\"0\",status:\"0\",tags:c.tags[m]});\n }\n \/\/ ⑦ 全频道通用:搜索发现(search_recommend 返回 $.data.books)\n head(\"▌ 全站 · 搜索发现\");\n row(\"猜你喜欢\",\"\/authopt\/search_recommend\",{ctype:c.ctype});\n return JSON.stringify(out);\n})()",
"header": "{\"User-Agent\": \"okhttp\/5.1.0\"}",
"jsLib": "\/\/ ===== 淘小说 11.2.8 公共签名库 =====\n\/\/ sign = MD5( 全部请求参数按 key 字典序排列、值用 UTF-8 明文拼接 + \"&key=mibook_123456\" ).toUpperCase()\n\/\/ 实锤: 112 条请求离线碰撞 112\/112 命中;App 内 java.md5Encode 复算一致。\n\/\/ 注意:sign 的是「解码后的参数值」;URL 里才 encodeURIComponent。\n\/\/ 注意:key 也要参与排序(当业务参数恰好含 key 时不可重复追加)。\nfunction TX_signBase(p) {\n var ks = Object.keys(p).sort();\n var s = '';\n for (var i = 0; i < ks.length; i++) {\n if (i) s += '&';\n s += ks[i] + '=' + String(p[ks[i]]);\n }\n return s;\n}\nfunction TX_query(p) {\n var ks = Object.keys(p).sort();\n var q = '';\n for (var i = 0; i < ks.length; i++) {\n if (i) q += '&';\n q += ks[i] + '=' + encodeURIComponent(String(p[ks[i]]));\n }\n return q;\n}\n\/\/ jv = java 绑定(规则 @js 环境),显式传入以兼容 jsLib 共享 scope\nfunction TX_url(jv, p, host, path) {\n var sign = String(jv.md5Encode(TX_signBase(p) + '&key=mibook_123456')).toUpperCase();\n return host + path + '?' + TX_query(p) + '&sign=' + sign;\n}\n\n\/\/ 11.2.8 真机抓包的固定设备\/游客参数。\n\/\/ 对照实验实锤:book_rank\/tf\/book\/tf\/chapter_content 只校验 sign,token↔uid 可任意;\n\/\/ 唯独 \/authopt\/ty\/search_books 额外校验 token↔uid 配对(伪造 → errcode 205 Invalid token)。\n\/\/ 故此处使用真机抓包的那组配对凭证,绝不自造。\nfunction TX_dev() {\n return {\n android_id: '76546fb5b7fa81c2',\n appid: 'mibook',\n brand: 'Xiaomi',\n channel: 'XiaoMi',\n device_id: '38825f4366714ddba026677ee69a004c',\n model: 'MI 8',\n optype: '0',\n ostype: '0',\n osversion: '10',\n package_name: 'com.martian.ttbook',\n token: 'a8976c97-ab2f-4bac-b4a6-389475e77719',\n uid: '85861337',\n version_code: '801',\n version_name: '11.2.8'\n };\n}\n\/\/ 服务端不校验 t 时效(2022 年旧时间戳 \/ 未来时间戳 \/ 当前时间戳 三组均 errcode:0),\n\/\/ 但仍每次用当前时间,避免任何潜在的日志侧异常。\nfunction TX_params(extra) {\n var p = TX_dev();\n if (extra) for (var k in extra) p[k] = extra[k];\n p.t = String(Date.now());\n return p;\n}\n\n\/\/ 面板切换的线路:sv = source 绑定,由规则 @js 显式传入。\n\/\/ ★jsLib 共享 scope 不注入任何规则环境绑定(java\/book\/source\/result\/baseUrl 全无,源码级实锤),\n\/\/ 所以本文件里所有函数一律「显式传参」,绝不裸用 source\/java。\nfunction TX_host(sv) {\n var d = '';\n try { d = String(sv.get('txApiHost') || ''); } catch (e) { d = ''; }\n if (!d || d.indexOf('http') !== 0) d = 'https:\/\/tybook.taoyuewenhua.net';\n return d.replace(\/\\\/+$\/, '');\n}\nfunction TX_cdn(sv) {\n var d = '';\n try { d = String(sv.get('txCdnHost') || ''); } catch (e) { d = ''; }\n if (!d || d.indexOf('http') !== 0) d = 'https:\/\/scdn.taoyuewenhua.net';\n return d.replace(\/\\\/+$\/, '');\n}\n\n\/\/ ===== 各规则入口(jv=java、sv=source,全部由规则 @js 显式传入) =====\n\/\/ 搜索:\/authopt\/ty\/search_books,Legado 的 page 从 1 开始,接口 page 从 0 开始\nfunction TX_search(jv, sv, key, page) {\n var p = TX_params({\n ctype: '1',\n keywords: String(key),\n page: String((Number(page) || 1) - 1),\n pageSize: '10',\n searchType: '1',\n sourceName: 'yw'\n });\n return TX_url(jv, p, TX_host(sv), '\/authopt\/ty\/search_books');\n}\n\/\/ 搜索\/发现列表里的 bookUrl:\/tf\/book?bid=xxx\nfunction TX_bookUrl(jv, sv, bid) {\n var p = TX_params({ bid: String(bid) });\n return TX_url(jv, p, TX_host(sv), '\/tf\/book');\n}\n\/\/ 详情 → 目录:\/tf\/chapter_list 302 跳到免签名 CDN,Legado 网络栈自动跟随\n\/\/ 极简参数报 errcode 202 Empty app id ⇒ 必须带 TX_dev() 全套参数(实锤)\nfunction TX_tocUrl(jv, sv, bid) {\n var p = TX_params({ bid: String(bid), retry: '0' });\n return TX_url(jv, p, TX_host(sv), '\/tf\/chapter_list');\n}\n\/\/ 目录章节 → 正文:\/tf\/chapter_content?bid=&cid=&autoBuy=true\nfunction TX_contentUrl(jv, sv, bid, cid) {\n var p = TX_params({ bid: String(bid), cid: String(cid), autoBuy: 'true' });\n return TX_url(jv, p, TX_host(sv), '\/tf\/chapter_content');\n}\n\/\/ 发现页:book_rank 榜单 \/ search_by_category 分类 \/ channel_books 书城频道 \/ search_recommend\n\/\/ 站点无「下一页」字段,终止判据用「本页返回空 \/ 与上一页内容重复」由 Legado 自己处理,\n\/\/ 不写死页码上限(用户明确要求:站点以后加书要能自动继续)。\nfunction TX_explore(jv, sv, path, extra, page) {\n var e = {};\n if (extra) for (var k in extra) e[k] = extra[k];\n e.page = String((Number(page) || 1) - 1);\n e.pageSize = '10';\n var p = TX_params(e);\n return TX_url(jv, p, TX_host(sv), path);\n}\n\n\/\/ 供用户粘贴 App 分享链接时反解 bid(bookUrlPattern 命中后由详情页规则使用)\nfunction TX_bidFrom(u) {\n var m = \/[?&]bid=([^&#]+)\/.exec(String(u));\n return m ? decodeURIComponent(m[1]) : '';\n}",
"lastUpdateTime": "1794548504444",
"loginUi": "[{\"name\": \"发布页\", \"type\": \"text\", \"viewName\": \"'发布页地址(官方落地页)'\", \"style\": {\"layout_flexBasisPercent\": 0.48, \"layout_flexGrow\": 1}}, {\"name\": \"应用发布页\", \"type\": \"button\", \"action\": \"TX_pubFrom(result)\", \"viewName\": \"'💾 应用发布页地址'\", \"style\": {\"layout_flexBasisPercent\": 0.48, \"layout_flexGrow\": 1}}, {\"name\": \"获取路线\", \"type\": \"button\", \"action\": \"TX_fetchPub()\", \"viewName\": \"'🧭 一键从发布页获取网址路线'\", \"style\": {\"layout_flexBasisPercent\": 1, \"layout_wrapBefore\": true}}, {\"name\": \"测试延时\", \"type\": \"button\", \"action\": \"TX_testAll()\", \"viewName\": \"'⏱ 检测路线延迟(ms写入路线按钮)'\", \"style\": {\"layout_flexBasisPercent\": 0.48, \"layout_flexGrow\": 1}}, {\"name\": \"检查登录状态\", \"type\": \"button\", \"action\": \"TX_checkLogin()\", \"viewName\": \"'🔍 检查登录状态\/游客凭证'\", \"style\": {\"layout_flexBasisPercent\": 0.48, \"layout_flexGrow\": 1}}, {\"name\": \"显示线路\", \"type\": \"button\", \"action\": \"TX_toast(TX_found().join(' \/ '))\", \"viewName\": \"'📋 显示当前全部路线'\", \"style\": {\"layout_flexBasisPercent\": 0.48, \"layout_flexGrow\": 1}}, {\"name\": \"打开网站\", \"type\": \"button\", \"action\": \"TX_web()\", \"viewName\": \"'🌐 打开官方落地页'\", \"style\": {\"layout_flexBasisPercent\": 0.48, \"layout_flexGrow\": 1}}, {\"name\": \"当前线路\", \"type\": \"button\", \"action\": \"\", \"viewName\": \"TX_curLineText()\", \"style\": {\"layout_flexBasisPercent\": 1, \"layout_wrapBefore\": true}}, {\"name\": \"路线标题\", \"type\": \"button\", \"action\": \"\", \"viewName\": \"'━━ 点击切换路线(延时见按钮名)━━'\", \"style\": {\"layout_flexBasisPercent\": 1, \"layout_wrapBefore\": true}}, {\"name\": \"路线1\", \"type\": \"button\", \"action\": \"TX_switch(1)\", \"viewName\": \"TX_lineBtn(1)\", \"style\": {\"layout_flexBasisPercent\": 0.48, \"layout_flexGrow\": 1}}, {\"name\": \"路线2\", \"type\": \"button\", \"action\": \"TX_switch(2)\", \"viewName\": \"TX_lineBtn(2)\", \"style\": {\"layout_flexBasisPercent\": 0.48, \"layout_flexGrow\": 1}}, {\"name\": \"路线3\", \"type\": \"button\", \"action\": \"TX_switch(3)\", \"viewName\": \"TX_lineBtn(3)\", \"style\": {\"layout_flexBasisPercent\": 0.48, \"layout_flexGrow\": 1}}, {\"name\": \"路线4\", \"type\": \"button\", \"action\": \"TX_switch(4)\", \"viewName\": \"TX_lineBtn(4)\", \"style\": {\"layout_flexBasisPercent\": 0.48, \"layout_flexGrow\": 1}}, {\"name\": \"路线5\", \"type\": \"button\", \"action\": \"TX_switch(5)\", \"viewName\": \"TX_lineBtn(5)\", \"style\": {\"layout_flexBasisPercent\": 0.48, \"layout_flexGrow\": 1}}, {\"name\": \"路线6\", \"type\": \"button\", \"action\": \"TX_switch(6)\", \"viewName\": \"TX_lineBtn(6)\", \"style\": {\"layout_flexBasisPercent\": 0.48, \"layout_flexGrow\": 1}}]",
"loginUrl": "\/\/ ========== 淘小说 11.2.8 运维面板 ==========\n\/\/ 全部函数挂在 loginUrl;SourceLoginDialog 用 \"$loginJS\\n$action\" 一起 eval。\n\/\/ 绑定差异(源码级实锤):loginUi<js> 无 source\/java;按钮 action 有 java(SourceLoginJsExtensions)\n\/\/ 且 source 仍绑定;viewName 表达式用 source 可用但不保证 java ⇒ 视图函数只读 source。\n\/\/ 兼容 LT(gedoor\/LegadoTeam) 与 E(Luoyacheng\/legado-E):V1 静态 loginUi 是 E 版唯一可用形态。\n\nvar TX_MAX = 6;\nvar TX_PUB_DEFAULT = 'https:\/\/m.taoyuewenhua.com';\nvar TX_API_DEFAULT = 'https:\/\/tybook.taoyuewenhua.net';\nvar TX_CDN_DEFAULT = 'https:\/\/scdn.taoyuewenhua.net';\nvar TX_UA = 'okhttp\/5.1.0';\n\/\/ 内置线路池:全部来自 2026-11-12 真机抓包 + 实测可达,不臆造域名。\n\/\/ ★不收录 https:\/\/app.taoyuewenhua.net:该域仅用于 assetlinks.json(manifest 实锤),\n\/\/ 实测根路径 403,放进线路池会误导用户切到不可用线路。\n\/\/ ★收录 m.taoyuewenhua.com 作为官方落地页(实测 200\/7069B),但它不是 API 线路,\n\/\/ 切换时 TX_switch 会把它当发布页展示,不会替换 txApiHost(见下)。\nvar TX_FALLBACK = [\n 'https:\/\/tybook.taoyuewenhua.net',\n 'https:\/\/scdn.taoyuewenhua.net',\n 'https:\/\/m.taoyuewenhua.com',\n 'http:\/\/scdn.taoyuewenhua.net'\n];\n\nfunction login() { return 'ok'; } \/\/ 必须实现,否则点右上角 √ 抛 \"Function login not implements!!!\"\n\nfunction TX_toast(t) {\n try { java.longToast(String(t)); } catch (e) { try { java.toast(String(t)); } catch (e2) {} }\n}\nfunction TX_gv(k, def) {\n var v = '';\n try { v = String(source.get(k)); } catch (e) { v = ''; }\n if (v === '' || v === 'null' || v === 'undefined') v = def || '';\n return v;\n}\nfunction TX_norm(u) {\n var s = String(u || '').trim().replace(\/\\\/+$\/, '');\n if (!s) return '';\n if (s.indexOf('http') !== 0) s = 'https:\/\/' + s;\n var m = \/^(https?:\\\/\\\/[^\/]+)\/.exec(s);\n return m ? m[1] : s;\n}\nfunction TX_of(u) {\n var m = \/^https?:\\\/\\\/([^\/]+)\/.exec(String(u));\n return m ? m[1] : String(u);\n}\nfunction TX_hdr() { return JSON.stringify({'User-Agent': TX_UA}); }\nfunction TX_refresh() {\n try { java.reLoginView(false); } catch (e) {\n try { java.reUiView(false); } catch (e2) {}\n }\n}\n\n\/\/ ---------- 发布页输入框 ----------\nfunction TX_pub() {\n var p = TX_norm(TX_gv('txPub', TX_PUB_DEFAULT));\n return p || TX_PUB_DEFAULT;\n}\nfunction TX_pubFrom(r) {\n var v = '';\n try { if (r && r.get) v = String(r.get('发布页') || ''); } catch (e0) {}\n if (!v) { try { v = String(r['发布页'] || ''); } catch (eA) {} }\n if (!v) { try { if (result && result.get) v = String(result.get('发布页') || ''); } catch (e1) {} }\n if (!v) { try { if (result) v = String(result['发布页'] || ''); } catch (eB) {} }\n if (!v || !v.trim()) v = TX_PUB_DEFAULT;\n var p = TX_norm(v);\n TX_toast('⏳ 正在应用发布页:' + TX_of(p) + ' …');\n try { source.put('txPub', p); } catch (e) { TX_toast('❌ 写入失败:' + e); return; }\n TX_toast('✅ 发布页已设为 ' + TX_of(p) + ',请点「🧭 一键从发布页获取网址路线」');\n TX_refresh();\n}\n\n\/\/ ---------- 线路池持久化 ----------\nfunction TX_setFound(list) {\n var out = [], i, u;\n for (i = 0; i < list.length && out.length < TX_MAX; i++) {\n u = TX_norm(list[i]);\n if (u && out.indexOf(u) < 0) out.push(u);\n }\n if (!out.length) out = TX_FALLBACK.slice(0);\n try { source.put('txFound', out.join(' ')); } catch (e) {}\n for (i = 0; i < TX_MAX; i++) { try { source.put('txDelay' + i, ''); } catch (e2) {} }\n return out;\n}\nfunction TX_found() {\n var s = TX_gv('txFound', '');\n if (!s) return TX_setFound(TX_FALLBACK);\n var a = s.split(\/\\s+\/), out = [], i;\n for (i = 0; i < a.length && out.length < TX_MAX; i++) {\n var u = TX_norm(a[i]);\n if (u.indexOf('http') === 0 && out.indexOf(u) < 0) out.push(u);\n }\n return out.length ? out : TX_setFound(TX_FALLBACK);\n}\n\/\/ ★不能用 .map(TX_host):jsLib 的 TX_host(sv) 需要显式 source 参数,作为 map 回调会拿到\n\/\/ (element,index,array) 三个实参导致返回错值甚至抛错,中断按钮后半段 Toast。统一改 TX_of。\nfunction TX_foundText() { return TX_found().map(TX_of).join(' \/ '); }\n\n\/\/ ---------- 🧭 一键从发布页获取网址路线 ----------\n\/\/ 分阶段 Toast:开始 → 抓取 → 解析 → 写池 → 完成 \/ 失败兜底\nfunction TX_fetchPub() {\n var pub = TX_pub();\n TX_toast('🧭 阶段1\/4:正在打开发布页 ' + TX_of(pub) + ' …');\n var raw = '', err = '', ms = 0;\n try {\n var t0 = Date.now();\n var r0 = java.connect(pub + '\/', TX_hdr(), 12000);\n ms = Date.now() - t0;\n raw = String(r0.body());\n if (r0.code && r0.code() !== 200) err = 'HTTP ' + r0.code();\n } catch (e) { err = String(e).substring(0, 90); }\n if (!err && (raw.length < 20 || raw.indexOf('Exception') === 0)) err = '内容异常(' + raw.length + 'B)';\n if (err) {\n TX_toast('⚠ 阶段1失败:' + err + ';载入内置 ' + TX_FALLBACK.length + ' 条兜底线路');\n TX_setFound(TX_FALLBACK);\n TX_toast('✅ 已载入内置线路,请点「⏱ 检测路线延迟」测速');\n TX_refresh(); return;\n }\n TX_toast('🔓 阶段2\/4:发布页已获取(' + raw.length + 'B \/ ' + ms + 'ms),正在解析同族域名…');\n var lines = [], m, u, hn;\n \/\/ 从发布页正文抽取 taoyuewenhua.com\/net 同族域名(排除纯资源\/图床)\n var re = \/https?:\\\/\\\/([a-z0-9][a-z0-9.\\-]*\\.taoyuewenhua\\.(?:com|net))\/gi;\n while ((m = re.exec(raw)) !== null) {\n u = TX_norm(m[0]); hn = TX_of(u);\n if (lines.indexOf(u) < 0) lines.push(u);\n }\n \/\/ 发布页自身 host 也算一条路线\n var self = TX_norm(pub);\n if (self && lines.indexOf(self) < 0) lines.push(self);\n \/\/ ★池首固定为「当前正在用的 API 主线路」,发布页解析出的域追加在后:\n \/\/ 避免用户打开面板看到的第 1 个按钮是落地页(不是 API),误点后以为切了线路。\n var lines2 = [TX_norm(TX_gv('txApiHost', TX_API_DEFAULT))];\n var i;\n for (i = 0; i < lines.length && lines2.length < TX_MAX; i++) {\n if (lines2.indexOf(lines[i]) < 0) lines2.push(lines[i]);\n }\n \/\/ 内置兜底补齐(真机抓包实锤的线路)\n for (i = 0; i < TX_FALLBACK.length && lines2.length < TX_MAX; i++) {\n if (lines2.indexOf(TX_FALLBACK[i]) < 0) lines2.push(TX_FALLBACK[i]);\n }\n lines = lines2;\n TX_toast('🔎 阶段3\/4:解析到 ' + lines.length + ' 条候选,正在写入线路按钮…');\n var got = TX_setFound(lines);\n TX_toast('✅ 阶段4\/4:共 ' + got.length + ' 条路线 → ' + got.map(TX_of).join(' \/ '));\n TX_toast('📌 延时尚未写入,请点「⏱ 检测路线延迟(ms写入路线按钮)」');\n TX_refresh();\n}\n\n\/\/ ---------- ⏱ 检测路线延迟(ms 写进各网址按钮) ----------\n\/\/ 判活按线路类型分别走真实可用路径(全部为非搜索接口,用户主动点击才触发):\n\/\/ · tybook API 线路 → 现算 sign 的 \/auth\/account.do(游客账户探针,返回 errcode:0)\n\/\/ · scdn CDN 线路 → 免签名静态目录 \/app\/chapters\/{bid}.json\n\/\/ · 官方落地页 m. → 根路径 HTML(200 \/ 7069B)\n\/\/ ★坑(实测):tybook 根路径返回 39B 的 {\"errcode\":202,\"errmsg\":\"Empty app id\"},\n\/\/ 用「根路径 + 长度阈值」判活会把正常 API 线路误标成「内容异常」;必须用带完整\n\/\/ 参数的签名接口。java.connect 失败时返回异常堆栈串而非抛异常,须显式排除。\nfunction TX_probeUrl(u) {\n if (u.indexOf('scdn.taoyuewenhua.net') > -1) {\n return u + '\/app\/chapters\/yw_20120838908848304.json';\n }\n if (u.indexOf('tybook.taoyuewenhua.net') > -1) {\n return TX_url(java, TX_params({}), u, '\/auth\/account.do');\n }\n return u + '\/';\n}\nfunction TX_testAll() {\n var list = TX_found();\n TX_toast('⏱ 阶段1\/3:开始测速,共 ' + list.length + ' 条路线(非搜索接口,不触发搜索风控)…');\n var i, r, b, ms, tag, best = -1, bestMs = 999999;\n for (i = 0; i < list.length; i++) {\n ms = -1; tag = '✗ 不可达';\n try {\n var t0 = Date.now();\n r = java.connect(TX_probeUrl(list[i]), TX_hdr(), 10000);\n b = String(r.body());\n if (b.indexOf('Exception') > -1 || b.indexOf('timeout') > -1) {\n tag = '✗ 不可达';\n } else if (r.code && (r.code() < 200 || r.code() >= 400)) {\n tag = '✗ HTTP\/' + r.code();\n } else if (b.indexOf('\"errcode\":0') > -1 || b.indexOf('\"chapterList\"') > -1 || b.length > 500) {\n ms = Date.now() - t0;\n try { if (r.callTime) { var ct = Number(r.callTime()); if (ct > 0) ms = ct; } } catch (eC) {}\n tag = ms + 'ms';\n if (ms < bestMs) { bestMs = ms; best = i; }\n } else {\n tag = '✗ 内容异常(' + b.length + 'B)';\n }\n } catch (e) { tag = '✗ ' + String(e).substring(0, 24); }\n try { source.put('txDelay' + i, tag); } catch (e2) {}\n TX_toast(' · 路线' + (i + 1) + ' ' + TX_of(list[i]) + ' → ' + tag);\n }\n TX_toast('📝 阶段2\/3:延时已写入各网址按钮,正在刷新面板…');\n if (best >= 0) {\n try { source.put('txBest', String(best)); } catch (e3) {}\n try { source.put('txDelay' + best, '⚡' + bestMs + 'ms'); } catch (e4) {}\n }\n TX_refresh();\n TX_toast('✅ 阶段3\/3:测速完成' + (best >= 0 ? ',最快 ' + TX_of(list[best]) + ' ⚡' + bestMs + 'ms(未自动切换,请手动点路线按钮)' : ',全部不可达'));\n}\n\n\/\/ ---------- 🔍 检查登录状态 ----------\n\/\/ 本源是游客态:无账号体系。该按钮给出「是否需要登录」的明确结论 + 凭证健康度。\n\/\/ 只打 \/auth\/account.do(非搜索接口),不会触发搜索风控。\nfunction TX_checkLogin() {\n TX_toast('🔍 阶段1\/3:正在检查游客凭证与接口连通性…');\n var p = TX_params({});\n var api = TX_gv('txApiHost', TX_API_DEFAULT);\n var u = TX_url(java, p, api, '\/auth\/account.do');\n var r = null, b = '', code = 0;\n try { r = java.connect(u, TX_hdr(), 10000); b = String(r.body()); code = Number(r.code()); }\n catch (e) { b = 'Exception ' + e; }\n var ok = code >= 200 && code < 400 && b.indexOf('\"errcode\":0') > -1;\n var isVip = \/\"isVip\":true\/.test(b);\n TX_toast('📡 阶段2\/3:\/auth\/account.do → HTTP ' + code + ' \/ ' + b.length + 'B');\n if (ok) {\n TX_toast('✅ 阶段3\/3:游客凭证有效,无需登录(isVip=' + isVip + ');搜索\/详情\/目录\/正文均可用');\n } else {\n TX_toast('❌ 阶段3\/3:接口异常 ' + b.slice(0, 100) + ';请在「路线」里切换到 ⚡ 最快线路后重试');\n }\n try { source.put('txLoginCheck', ok ? '游客态正常' : '异常'); } catch (e2) {}\n TX_refresh();\n}\nfunction TX_web() {\n TX_toast('🌐 正在打开官方落地页 ' + TX_of(TX_pub()) + ' …');\n try { java.openUrl(TX_pub()); TX_toast('✅ 已调起浏览器'); }\n catch (e) { TX_toast('⚠ 无法调起浏览器:' + String(e).slice(0, 80) + ';地址:' + TX_pub()); }\n}\n\n\/\/ ---------- 线路切换(各网站一个按钮) ----------\n\/\/ 切线路时同时写 txApiHost \/ txCdnHost:若选中的是 scdn 则只切 CDN,API 保持默认;\n\/\/ 其余线路切 API。这样「各网站点击切换」对真实生效的域名语义正确。\nfunction TX_switch(n) {\n var list = TX_found();\n if (n < 1 || n > list.length) { TX_toast('⚠ 路线' + n + ' 不存在,当前只有 ' + list.length + ' 条'); return; }\n var u = list[n - 1];\n TX_toast('⏳ 正在切换到路线' + n + ':' + TX_of(u) + ' …');\n try {\n if (u.indexOf('scdn.taoyuewenhua.net') > -1) {\n \/\/ CDN 线路:只切目录域名,API 保持默认\n source.put('txCdnHost', u);\n source.put('txApiHost', TX_API_DEFAULT);\n } else if (u.indexOf('tybook.taoyuewenhua.net') > -1) {\n \/\/ 主 API 线路:切 API,CDN 回默认\n source.put('txApiHost', u);\n source.put('txCdnHost', TX_CDN_DEFAULT);\n } else {\n \/\/ 官方落地页等「非 API 域」:只记发布页,不动 API\/CDN,\n \/\/ 避免用户误切到 403 的 assetlinks 域导致整源不可用。\n source.put('txPub', u);\n }\n source.put('txLine', String(n));\n } catch (e) { TX_toast('❌ 写入失败:' + e); return; }\n TX_toast('✅ 已切换到 ' + TX_of(u) + ';请重新搜索\/刷新书城验证');\n TX_refresh();\n}\n\n\/\/ ---------- viewName 动态文本(只读 source,不碰 java) ----------\n\/\/ ★viewName 为 JS 表达式时,求值结果经 normalizeJsResult「原样显示、不剥引号」\n\/\/ (源码级实锤),因此这两个函数返回纯文本,禁止自带外层单引号。\nfunction TX_lineBtn(n) {\n var list = TX_found();\n if (n > list.length) return '— 备用位 —';\n var u = list[n - 1];\n var d = TX_gv('txDelay' + (n - 1), '');\n var cur = '';\n try { cur = String(source.get('txLine') || ''); } catch (e) {}\n var mark = (String(n) === cur) ? '● ' : '○ ';\n return mark + TX_of(u) + (d ? ' ' + d : '');\n}\nfunction TX_curLineText() {\n var api = TX_gv('txApiHost', TX_API_DEFAULT);\n var cdn = TX_gv('txCdnHost', TX_CDN_DEFAULT);\n var st = TX_gv('txLoginCheck', '未检查');\n return '当前 API:' + TX_of(api) + ' | CDN:' + TX_of(cdn) + ' | 状态:' + st;\n}",
"respondTime": 180000,
"ruleBookInfo": {
"author": "$.authorName",
"coverUrl": "$.coverUrl",
"init": "$.data",
"intro": "<br>\n📂 分类:{{$.categoryName}}\n🎗 标签:{{$.tagList##\\s##,}}\n👤 主角:{{$.roleList##\\s##,}}\n🕰 更新:{{java.timeFormat(java.getString('$.latestChapterUpdateTime'))}}\n📖 字数:{{$.allWords}}\n📝 简介:\n{{$.intro}}",
"kind": "{{$.categoryName}}\n{{java.getString('$.status')=='50'?'完结':'连载'}}\n{{$.tagList##\\s##·}}",
"lastChapter": "$.latestChapter",
"name": "$.title",
"tocUrl": "@js:\n\/\/ {{$.bid}} 由 Legado 模板先求值,再进 @js\nTX_tocUrl(java, source, '{{$.bid}}');",
"wordCount": "$.allWords"
},
"ruleContent": {
"content": "$.data.content"
},
"ruleExplore": {
"author": "$.authorName",
"bookList": "$.data.bookList||$.data.books",
"bookUrl": "sourceId@js:\n\/\/ result = 该条书籍 JSON 的 sourceId 字段值(前缀规则 sourceId@js: 已取出)\nTX_bookUrl(java, source, result);",
"coverUrl": "$.coverUrl",
"intro": "$.intro",
"kind": "{{$.categoryName}}\n{{java.getString('$.status')=='50'?'完结':'连载'}}\n{{$.tagList##\\s##·}}",
"name": "$.title",
"wordCount": "$.allWords"
},
"ruleSearch": {
"author": "$.authorName",
"bookList": "$.data.bookList",
"bookUrl": "sourceId@js:\n\/\/ result = 该条书籍 JSON 的 sourceId 字段值(前缀规则 sourceId@js: 已取出)\nTX_bookUrl(java, source, result);",
"coverUrl": "$.coverUrl",
"intro": "$.intro",
"kind": "{{$.categoryName}}\n{{java.getString('$.status')=='50'?'完结':'连载'}}\n{{$.tagList##\\s##·}}",
"name": "$.title",
"wordCount": "$.allWords"
},
"ruleToc": {
"chapterList": "$.data.chapterList",
"chapterName": "$.title",
"chapterUrl": "@js:\nTX_contentUrl(java, source, '{{$.bid}}', '{{$.cid}}');",
"updateTime": "{{$.words}} 字 · {{java.timeFormat(java.getString('$.modifiedOn'))}}"
},
"searchUrl": "@js:\nTX_search(java, source, key, page);",
"variableComment": "本源无需填写变量;线路切换请用「菜单→登录」里的路线按钮。",
"weight": 0
}