[{"bookSourceComment":"\/\/ ===== 哔咔漫画 自包含书源 v84 =====\n\/\/ 官方 App 接口 picaapi.picacomic.com，\n\/\/ 签名：HMAC-SHA256(path+time+nonce+method+apiKey, secret)，raw 转小写\n\/\/\n\/\/ 【重要】本站已强制登录：未登录时连 categories 都返回 401 error 1005\n\/\/ 首次使用：长按书源→登录→填账号密码→点「🔑登录」\n\/\/\n\/\/ 【v84 新增 1：封面修复】\n\/\/ 根因：封面走 Glide（OkHttpStreamFetcher → AnalyzeUrl.getGlideUrl），\n\/\/      getGlideUrl 只保留 url+header，dnsIp 选项被丢弃，\n\/\/      所以正文能显示而封面全灰；官方 storage-b 域名在部分网络下 IPv6 优先且不可达。\n\/\/ 方案：把 tobeimg 的 base64 解出真实 \/static\/ 路径（避开 302 跳 img.picacomic.com），\n\/\/      再用 http:\/\/<CF-IP><path> + {\"headers\":{\"Host\":\"storage-b.picacomic.com\"}} 直连，\n\/\/      Host 头在 Glide\/OkHttp 链路中保留（BridgeInterceptor 只在缺失时补 Host），\n\/\/      不依赖系统 DNS；登录头只发给同站，不会外泄。\n\/\/ 三种封面模式可切换（登录面板「封面模式」）：\n\/\/      🚀IP直连 = http:\/\/<IP>+Host 头（默认，抗 IPv6\/DNS 问题）\n\/\/      🌐域名直连 = https:\/\/storage-b...（网络正常时用）\n\/\/      🪞镜像站 = 参考源同款 s3.go2778.com\n\/\/\n\/\/ 【v84 新增 2：源站更新 + 选源站】\n\/\/ 「🔄源站更新」= 拉官方 http:\/\/68.183.234.72\/init，取 addresses 存 picab_official_ips，\n\/\/      按当前线路重建 IP 池 picab_pool，并刷新发现页签名。\n\/\/ 「①官方init \/ ②Cloudflare \/ ③系统DNS」= 切换线路：\n\/\/      ①官方init = 官方 init IP 优先 + CF 池兜底\n\/\/      ②Cloudflare = 10 个实测可连通 CF IP\n\/\/      ③系统DNS = 不设 dnsIp，走系统解析（IPv6 正常时用）\n\/\/ 「📋查看当前」= 显示当前线路 \/ IP 池 \/ 官方 init \/ 封面模式。\n\/\/ 线路存 picab_station（0\/1\/2），池存 picab_pool（\"none\"=系统DNS），删书源会清空。\n\/\/\n\/\/ 【正文图片】src 自带 10 个 Cloudflare IPv4 联合池并按图轮换首选顺序；\n\/\/      Legado 的 retry 只重试非 2xx，对 IOException 无效。\n\/\/ 旧书修复：长按书籍 → 删除 → 重新搜索加入（正文缓存按书重建）。\n\/\/\n\/\/ 【已知遗留】check_source 的域名可访问性探测与 debug_source 的首次裸请求走系统 DNS，\n\/\/      IPv6 超时会显示「网站失效」——这是校验器\/调试器限制，\n\/\/      真实使用（搜索\/详情\/目录\/正文\/发现页）走书源规则，正常。\n","bookSourceGroup":"漫画,AI,R18","bookSourceName":"哔咔漫画〔自包含〕","bookSourceType":2,"bookSourceUrl":"https:\/\/picaapi.picacomic.com##防同","bookUrlPattern":".*:\\\/\\\/.*\\\/comics\\\/[a-z0-9]+","concurrentRate":"1\/1200","customButton":false,"customOrder":1,"enabled":true,"enabledCookieJar":false,"enabledExplore":true,"eventListener":false,"exploreUrl":"<js>\nvar _out = [];\nvar _at = String.fromCharCode(64,106,115,58);\nvar _sg = '(function(){ var K=\"C69BAF41DA5ABD1FFEDC6D2FEA56B\"; var S=\"~d}$Q7$eIni=V)9\"+String.fromCharCode(92)+\"RK\/P.RM4;9[7|@\/CA}b~OW!3?EV\"+String.fromCharCode(96,58,60,62)+\"M7pddUBL5n|0\/*Cn\"; var PL=\"\"; try{PL=String(source.get(\"picab_pool\")||\"\");}catch(e0){} if(PL===\"none\"){PL=\"\";}else if(!PL){PL=\"172.67.194.19,172.67.80.1,104.21.235.3,104.21.235.4,104.21.91.145,104.19.53.76,104.21.80.1,104.21.81.1,104.16.0.1,104.17.0.1\";} var pre=String(result); var a=pre.substring(pre.indexOf(\":\/\/\")+3); var p=a.substring(a.indexOf(\"\/\")); if(p.charAt(0)===\"\/\"){p=p.substring(1);} var ts=String(Math.floor(Date.now()\/1000)); var nc=\"\"; for(var i=0;i<32;i++)nc+=\"0123456789abcdef\".charAt(Math.floor(Math.random()*16)); var raw=(p+ts+nc+\"GET\"+K).toLowerCase(); var m=Packages.javax.crypto.Mac.getInstance(\"HmacSHA256\"); m.init(new Packages.javax.crypto.spec.SecretKeySpec(new Packages.java.lang.String(S).getBytes(\"UTF-8\"),\"HmacSHA256\")); var b=m.doFinal(new Packages.java.lang.String(raw).getBytes(\"UTF-8\")); var sg=\"\"; for(var i=0;i<b.length;i++){var x=b[i]&0xff;sg+=(x<16?\"0\":\"\")+x.toString(16);} var h={\"User-Agent\":\"okhttp\/3.8.1\",\"accept\":\"application\/vnd.picacomic.com.v1+json\",\"api-key\":K,\"app-channel\":\"2\",\"app-version\":\"2.2.1.2.3.3\",\"app-uuid\":\"defaultUuid\",\"app-platform\":\"android\",\"app-build-version\":\"44\",\"image-quality\":\"original\",\"time\":ts,\"nonce\":nc,\"signature\":sg}; var au=\"\"; try{au=String(source.get(\"picab_token\"));}catch(e){} if(au&&au!==\"null\"&&au!==\"\"){h[\"authorization\"]=au;} var opt={method:\"GET\",timeout:25000,retry:0,headers:h}; if(PL){opt.dnsIp=PL;} return \"https:\/\/picaapi.picacomic.com\/\"+p+\",\"+JSON.stringify(opt); })()';\nfunction _mk(path) {\n  return \"https:\/\/picaapi.picacomic.com\/\" + path + _at + _sg;\n}\nfunction push(title, path) {\n  var t = title;\n  try {\n    var st = String(source.get(\"picaTC\") || \"1\");\n    if (st === \"1\") { t = java.t2s(t); }\n  } catch (e0) {}\n  _out.push({ title: t, url: _mk(path),\n              style: { layout_flexGrow: 1, layout_flexBasisPercent: 0.25 } });\n}\nfunction group(title) {\n  _out.push({ title: title, url: \"\",\n              style: { layout_flexGrow: 1, layout_flexBasisPercent: 1 } });\n}\nfunction sortName() {\n  var o = String(source.get(\"picaSort\") || \"0\");\n  return ({ \"0\": \"dd\", \"1\": \"da\", \"2\": \"ld\", \"3\": \"vd\" })[o] || \"dd\";\n}\nvar _cats = JSON.parse(picac_get(\"categories\", source, java)).data.categories || [];\ngroup(\"▂▂ 分 类 ▂▂\");\n_cats.forEach(function (c) {\n  if (c.isWeb) { return; }\n  push(c.title, \"comics?page=1&c=\" + encodeURIComponent(c.title) + \"&s=\" + sortName());\n});\ngroup(\"▂▂ 排 行 榜 ▂▂\");\npush(\"24小时热榜\", \"comics\/leaderboard?tt=H24&ct=VC\");\npush(\"本周热榜\", \"comics\/leaderboard?tt=D7&ct=VC\");\npush(\"本月热榜\", \"comics\/leaderboard?tt=D30&ct=VC\");\ngroup(\"▂▂ 全 站 排 序 ▂▂\");\npush(\"最新发布\", \"comics?page=1&s=dd\");\npush(\"最早发布\", \"comics?page=1&s=da\");\npush(\"最多爱心\", \"comics?page=1&s=ld\");\npush(\"最多观看\", \"comics?page=1&s=vd\");\ngroup(\"▂▂ 更 多 ▂▂\");\npush(\"🎲随机漫画\", \"comics\/random\");\npush(\"❤我的收藏\", \"users\/favourite?s=\" + sortName() + \"&page=1\");\npush(\"📚本子馆藏\", \"collections\");\npush(\"👑骑士榜\", \"comics\/knight-leaderboard\");\nString(JSON.stringify(_out));\n<\/js>","header":"{\n  \"User-Agent\": \"okhttp\/3.8.1\",\n  \"accept\": \"application\/vnd.picacomic.com.v1+json\",\n  \"api-key\": \"C69BAF41DA5ABD1FFEDC6D2FEA56B\",\n  \"app-channel\": \"2\",\n  \"app-version\": \"2.2.1.2.3.3\",\n  \"app-uuid\": \"defaultUuid\",\n  \"app-platform\": \"android\",\n  \"app-build-version\": \"44\",\n  \"image-quality\": \"original\"\n}","jsLib":"\/\/ ===== 哔咔漫画 自包含库 (v84) =====\n\/\/ 作用域铁律：jsLib 在独立 ScriptBindings 求值，只有函数定义被 chainTo 共享；\n\/\/ source\/java\/baseUrl\/result 在此不存在 => 一律参数注入 (SRC, JV)，禁止裸引用。\n\/\/ 本次全部改名（picab_ -> picac_ \/ PICAB_ -> PICAC_），规避 Rhino SharedJsScope 编译缓存。\nvar PICA = {\n  API_KEY: \"C69BAF41DA5ABD1FFEDC6D2FEA56B\",\n  SECRET: \"~d}$Q7$eIni=V)9\" + String.fromCharCode(92) + \"RK\/P.RM4;9[7|@\/CA}b~OW!3?EV\" + String.fromCharCode(96, 58, 60, 62) + \"M7pddUBL5n|0\/*Cn\",\n  BASE: \"https:\/\/picaapi.picacomic.com\/\",\n  DNS_POOL: [\"172.67.194.19\", \"172.67.80.1\", \"104.21.235.3\",\n             \"104.21.235.4\", \"104.21.91.145\", \"104.19.53.76\",\n             \"104.21.80.1\", \"104.21.81.1\", \"104.16.0.1\", \"104.17.0.1\"],\n  INIT_URL: \"http:\/\/68.183.234.72\/init\",\n  MIRROR: \"https:\/\/s3.go2778.com\"\n};\n\nfunction picac_hash(s) {\n  var h = 0;\n  s = String(s || \"\");\n  for (var i = 0; i < s.length; i++) {\n    h = ((h << 5) - h + s.charCodeAt(i)) | 0;\n  }\n  return Math.abs(h);\n}\n\nfunction picac_bytes(str) {\n  return new Packages.java.lang.String(str).getBytes(\"UTF-8\");\n}\n\nfunction picac_hmac(raw, key) {\n  var m = Packages.javax.crypto.Mac.getInstance(\"HmacSHA256\");\n  m.init(new Packages.javax.crypto.spec.SecretKeySpec(picac_bytes(key), \"HmacSHA256\"));\n  var b = m.doFinal(picac_bytes(raw));\n  var s = \"\";\n  for (var i = 0; i < b.length; i++) {\n    var x = b[i] & 0xff;\n    s += (x < 16 ? \"0\" : \"\") + x.toString(16);\n  }\n  return s;\n}\n\nfunction picac_nonce() {\n  var s = \"\";\n  for (var i = 0; i < 32; i++) {\n    s += \"0123456789abcdef\".charAt(Math.floor(Math.random() * 16));\n  }\n  return s;\n}\n\nfunction picac_token(SRC) {\n  try {\n    var h = String(SRC.getLoginHeader() || \"\");\n    if (h && h !== \"null\") {\n      var m = h.match(new RegExp('\"authorization\"\\\\s*:\\\\s*\"([^\"]+)\"'));\n      if (m) { return m[1]; }\n      return h;\n    }\n  } catch (e0) {}\n  try { return String(SRC.get(\"picab_token\") || \"\"); } catch (e1) { return \"\"; }\n}\n\nfunction picac_official(SRC) {\n  try { return String(SRC.get(\"picab_official_ips\") || \"\"); } catch (e0) { return \"\"; }\n}\n\nfunction picac_pool(SRC) {\n  var p = \"\";\n  try { p = String(SRC.get(\"picab_pool\") || \"\"); } catch (e0) {}\n  if (p === \"none\") { return \"\"; }\n  if (p) { return p; }\n  return PICA.DNS_POOL.join(\",\");\n}\n\nfunction picac_stationName(idx) {\n  return ({ \"0\": \"官方init\", \"1\": \"Cloudflare\", \"2\": \"系统DNS直连\" })[String(idx)] || \"未知\";\n}\n\nfunction picac_currentStation(SRC) {\n  try { return String(SRC.get(\"picab_station\") || \"1\"); } catch (e0) { return \"1\"; }\n}\n\nfunction picac_coverMode(SRC) {\n  try {\n    var m = String(SRC.get(\"picab_cover_mode\") || \"ip\");\n    return (m === \"dns\" || m === \"mirror\") ? m : \"ip\";\n  } catch (e0) { return \"ip\"; }\n}\n\nfunction picac_buildPool(station, official) {\n  var out = [];\n  function add(ip) {\n    ip = String(ip || \"\").trim();\n    if (ip && ip.indexOf(\".\") > 0 && out.indexOf(ip) < 0) { out.push(ip); }\n  }\n  if (station === \"0\") {\n    String(official || \"\").split(\",\").forEach(add);\n    PICA.DNS_POOL.forEach(add);\n  } else if (station === \"1\") {\n    PICA.DNS_POOL.forEach(add);\n  } else {\n    return \"none\";\n  }\n  return out.join(\",\");\n}\n\nfunction picac_headers(path, method, SRC, JV) {\n  var ts = String(Math.floor(Date.now() \/ 1000));\n  var nonce = picac_nonce();\n  var raw = (path + ts + nonce + method + PICA.API_KEY).toLowerCase();\n  var sig = picac_hmac(raw, PICA.SECRET);\n  var h = {\n    \"User-Agent\": \"okhttp\/3.8.1\",\n    \"accept\": \"application\/vnd.picacomic.com.v1+json\",\n    \"api-key\": PICA.API_KEY,\n    \"app-channel\": \"2\",\n    \"app-version\": \"2.2.1.2.3.3\",\n    \"app-uuid\": \"defaultUuid\",\n    \"app-platform\": \"android\",\n    \"app-build-version\": \"44\",\n    \"image-quality\": \"original\",\n    \"time\": ts,\n    \"nonce\": nonce,\n    \"signature\": sig\n  };\n  var tk = picac_token(SRC);\n  if (tk && tk !== \"null\" && tk !== \"\") { h[\"authorization\"] = tk; }\n  return h;\n}\n\nfunction picac_opt(path, opt, SRC, JV) {\n  opt = opt || {};\n  opt.headers = picac_headers(path, opt.method || \"GET\", SRC, JV);\n  if ((opt.method || \"GET\") === \"POST\") {\n    opt.headers[\"Content-Type\"] = \"application\/json; charset=UTF-8\";\n  }\n  var dp = picac_pool(SRC);\n  if (dp && !opt.dnsIp) { opt.dnsIp = dp; }\n  if (!opt.retry) { opt.retry = 4; }\n  if (!opt.timeout) { opt.timeout = 25000; }\n  return PICA.BASE + path + \",\" + JSON.stringify(opt);\n}\n\nfunction picac_get(path, SRC, JV) {\n  var last = \"\";\n  for (var i = 0; i < 3; i++) {\n    last = String(JV.ajax(picac_opt(path, { method: \"GET\" }, SRC, JV)) || \"\");\n    if (last.charAt(0) === \"{\") { return last; }\n    if (i < 2) {\n      try { Packages.java.lang.Thread.sleep(1500); } catch (e0) {}\n    }\n  }\n  return last;\n}\n\nfunction picac_post(path, bodyObj, SRC, JV) {\n  var last = \"\";\n  for (var i = 0; i < 3; i++) {\n    last = String(JV.ajax(picac_opt(path, { method: \"POST\", body: JSON.stringify(bodyObj) }, SRC, JV)) || \"\");\n    if (last.charAt(0) === \"{\") { return last; }\n    if (i < 2) {\n      try { Packages.java.lang.Thread.sleep(1500); } catch (e0) {}\n    }\n  }\n  return last;\n}\n\/\/ ---------- 封面 ----------\n\/* 解析 thumb：\n   tobeimg\/<hash>\/rs:fill:W:H:0\/g:sm\/<base64>[.jpg]  → base64 解出真实图片 URL\n   tobs\/<file>                                          → \/static\/<file>（去掉 tobs\/ 前缀）\n   <file>                                               → \/static\/<file>\n   返回 {host, path, raw, real, isSB} *\/\nfunction picac_parseThumb(media) {\n  var out = { host: \"\", path: \"\", raw: \"\", real: \"\", isSB: false };\n  if (!media) { return out; }\n  var fs = String(media.fileServer || \"\").replace(new RegExp(\"\/$\"), \"\");\n  var path = String(media.path || \"\").replace(new RegExp(\"^\/?\"), \"\");\n  out.raw = path;\n  if (!path) { return out; }\n  if (path.indexOf(\"tobeimg\/\") === 0) {\n    var idx = path.indexOf(\"\/g:sm\/\");\n    if (idx >= 0) {\n      var b64 = path.substring(idx + 6);\n      var m = b64.match(new RegExp(\"^([A-Za-z0-9+\/=]+)(\\\\.[A-Za-z0-9]+)?$\"));\n      if (m) {\n        try {\n          var pay = m[1];\n          while (pay.length % 4 !== 0) { pay += \"=\"; }\n          var real = String(new Packages.java.lang.String(\n              Packages.android.util.Base64.decode(pay, 0), \"UTF-8\"));\n          var i2 = real.indexOf(\"\/static\/\");\n          if (i2 >= 0) {\n            out.real = real;\n            out.host = real.substring(0, i2).replace(new RegExp(\"^https?:\/\/\"), \"\");\n            out.path = real.substring(i2);\n            out.isSB = out.host.indexOf(\"storage-b\") >= 0;\n            return out;\n          }\n        } catch (e0) {}\n      }\n    }\n  }\n  out.host = fs.replace(new RegExp(\"^https?:\/\/\"), \"\");\n  if (path.indexOf(\"tobs\/\") === 0) { path = path.substring(5); }\n  out.path = \"\/static\/\" + path;\n  out.isSB = out.host.indexOf(\"storage-b\") >= 0;\n  return out;\n}\n\n\/* 图片 URL（封面与正文共用一套规则）\n   关键：漫画浏览模式走 Glide → AnalyzeUrl.getGlideUrl()，\n        该方法【只保留 url + headerMap，丢弃 dnsIp】⇒\n        域名直连在 IPv6 优先的网络里必然超时。\n        所以图片 URL 必须自带 IP 直连能力：\n        storage-b → http:\/\/<CF-IP><path> + {\"headers\":{\"Host\":\"storage-b.picacomic.com\"}}\n        Host 头在 OkHttp BridgeInterceptor 只在缺失时补 ⇒ 保留；\n        同时对普通浏览模式（getByteArrayAwait，dnsIp 生效）也完全兼容。\n   其它域（storage1 等）真机不可达 ⇒ 回退域名直连。 *\/\nfunction picac_imgUrl(media, SRC) {\n  var t = picac_parseThumb(media);\n  if (!t.path) { return \"\"; }\n  var mode = \"ip\";\n  try { mode = picac_coverMode(SRC); } catch (e0) {}\n  if (mode === \"dns\") {\n    return \"https:\/\/\" + t.host + t.path;\n  }\n  if (t.isSB) {\n    var ips = PICA.DNS_POOL;\n    var ip = ips[picac_hash(t.path) % ips.length];\n    return \"http:\/\/\" + ip + t.path\n         + ',{\"method\":\"GET\",\"timeout\":30000,\"headers\":{\"Host\":\"' + t.host + '\"}}';\n  }\n  return \"https:\/\/\" + t.host + t.path;\n}\n\n\/* 封面 URL（三种模式，由 picab_cover_mode 控制）：\n   ip     → storage-b 用 IP+Host 头；其它域域名直连\n   dns    → 全部域名直连\n   mirror → 参考源同款镜像 s3.go2778.com（保留原始 tobeimg 路径） *\/\nfunction picac_cover(media, SRC) {\n  var t = picac_parseThumb(media);\n  if (!t.raw) { return \"\"; }\n  var mode = picac_coverMode(SRC);\n  if (mode === \"mirror\") {\n    return PICA.MIRROR + \"\/static\/\" + t.raw;\n  }\n  if (!t.host) { return \"\"; }\n  if (mode === \"dns\") {\n    return \"https:\/\/\" + t.host + t.path;\n  }\n  if (t.isSB) {\n    var ips = PICA.DNS_POOL;\n    var ip = ips[picac_hash(t.path) % ips.length];\n    return \"http:\/\/\" + ip + t.path\n         + ',{\"method\":\"GET\",\"timeout\":20000,\"headers\":{\"Host\":\"' + t.host + '\"}}';\n  }\n  return \"https:\/\/\" + t.host + t.path;\n}\n\n\/* 正文图片 URL：与封面同规则（IP+Host 头），保证漫画浏览模式也能加载 *\/\nfunction picac_img(media, SRC) {\n  return picac_imgUrl(media, SRC);\n}\n\nfunction picac_bookid(u) {\n  var s = String(u || \"\");\n  var c = s.indexOf(\",{\");\n  if (c >= 0) { s = s.substring(0, c); }\n  var m = s.match(new RegExp(\"comics\/([0-9a-fA-F]{24})\"));\n  if (m) { return m[1]; }\n  m = s.match(new RegExp(\"^([0-9a-fA-F]{24})$\"));\n  return m ? m[1] : \"\";\n}\n\nfunction picac_item(c, SRC, JV) {\n  if (!c) { return null; }\n  return {\n    name: c.title || \"\",\n    author: c.author || \"\",\n    cover: picac_cover(c.thumb, SRC),\n    intro: \"\",\n    kind: [(c.__group || \"\"),\n           String(c.categories || []).replace(\/,\/g, \" \"),\n           c.finished ? \"完结\" : \"连载\",\n           c.likesCount ? c.likesCount + \"♥\" : \"\"].filter(Boolean).join(\" · \"),\n    latest: \"\",\n    word: \"\",\n    url: PICA.BASE + \"comics\/\" + (c._id || \"\")\n        + ',{\"method\":\"GET\",\"retry\":3,\"timeout\":20000,'\n        + '\"headers\":' + JSON.stringify(picac_headers(\"comics\/\" + (c._id || \"\"), \"GET\", SRC))\n        + ',\"dnsIp\":\"' + picac_pool(SRC) + '\"}'\n  };\n}\n\nfunction picac_list(data) {\n  var d = data || {};\n  if (d.collections) {\n    var flat = [];\n    (d.collections || []).forEach(function (g) {\n      (g.comics || []).forEach(function (c) {\n        c.__group = g.title;\n        flat.push(c);\n      });\n    });\n    return flat;\n  }\n  var list = d.comics ? (d.comics.docs || d.comics) : (d.docs || d);\n  if (!Array.isArray(list)) { list = []; }\n  return list;\n}\n\nfunction picac_say(jv, msg) {\n  var stamped = \"[\" + new Date().toTimeString().substring(0, 8) + \"] \" + msg;\n  try { jv.upLoginData({ \"登录状态\": stamped }); } catch (e1) {}\n  try { jv.toast(stamped); } catch (e2) {}\n  try { jv.longToast(stamped); } catch (e3) {}\n  try { jv.reLoginView(false); } catch (e4) {}\n}\nfunction PICAC_checkLogin(jv, SRC) {\n  var msg = \"\";\n  try {\n    var txt = String(jv.ajax(picac_opt(\"users\/profile\", { method: \"GET\" }, SRC, jv)) || \"\");\n    if (!txt) { msg = \"检查失败: 无响应\"; }\n    else if (txt.charAt(0) !== \"{\") {\n      msg = \"❌ 检查失败: 网络超时（试试切换源站）\";\n      picac_say(jv, msg); return;\n    } else {\n      var jo = null;\n      try { jo = JSON.parse(txt); } catch (e3) {}\n      if (jo && jo.code === 200 && jo.data && jo.data.user) {\n        var u = jo.data.user;\n        msg = \"✅ 已登录: \" + (u.name || u.email || \"未知\")\n              + \" (Lv.\" + (u.level || \"?\") + \")\"\n              + (u.isPunched ? \" [今日已签到]\" : \" [今日未签到]\");\n      } else if (jo && (jo.code === 401 || jo.code === 403)) {\n        msg = \"❌ 未登录，请填账号密码后点右上角✓登录\";\n      } else {\n        msg = \"⚠️ 状态未知: \" + txt.substring(0, 60);\n      }\n    }\n  } catch (e) { msg = \"检查失败: \" + e; }\n  picac_say(jv, msg);\n}\n\nfunction PICAC_punch(jv, SRC) {\n  var msg = \"\";\n  try {\n    var txt = String(jv.ajax(picac_opt(\"users\/punch-in\",\n        { method: \"POST\", body: \"{}\" }, SRC, jv)) || \"\");\n    if (!txt) { msg = \"签到失败: 无响应\"; }\n    else if (txt.charAt(0) !== \"{\") {\n      var why = txt.indexOf(\"SocketTimeout\") >= 0 ? \"网络超时\" : \"网络错误\";\n      msg = \"❌ 签到失败: \" + why + \"（可试「🔄源站更新」或切换源站）\";\n      picac_say(jv, msg); return;\n    }\n    var jo = null;\n    try { jo = JSON.parse(txt); } catch (e0) {}\n    if (jo && jo.code === 200 && jo.data && jo.data.res) {\n      var st = String(jo.data.res.status || \"\");\n      if (st === \"ok\") { msg = \"✅ 签到成功 (最近签到日 \" + (jo.data.res.punchInLastDay || \"?\") + \")\"; }\n      else if (st === \"fail\") { msg = \"📌 今日已签到，无需重复\"; }\n      else { msg = \"签到状态: \" + st; }\n    } else if (jo && (jo.code === 401 || jo.code === 403)) {\n      msg = \"❌ 未登录，请先登录再签到\";\n    } else {\n      msg = \"❌ 签到失败: \" + (jo && jo.message ? jo.message : txt.substring(0, 80));\n    }\n  } catch (e) { msg = \"签到异常: \" + e; }\n  picac_say(jv, msg);\n}\n\nfunction PICAC_logout(jv, SRC) {\n  try { SRC.putLoginHeader(\"\"); } catch (e0) {}\n  try { SRC.put(\"picab_token\", \"\"); } catch (e5) {}\n  picac_say(jv, \"已退出登录（凭据已清除）\");\n}\n\nfunction PICAC_refreshExplore(jv, SRC) {\n  var msg = \"\";\n  try {\n    SRC.refreshExplore();\n    msg = \"✅ 发现页已刷新（重新生成签名，请回到发现页）\";\n  } catch (e) { msg = \"刷新失败: \" + e; }\n  picac_say(jv, msg);\n}\n\n\/* 源站更新：拉官方 init 地址 → 写入 IP 池 → 刷新发现页 *\/\nfunction PICAC_updateStation(jv, SRC) {\n  var msg = \"\";\n  var r = \"\";\n  try {\n    r = String(jv.ajax(PICA.INIT_URL + \"?t=\" + String(Math.floor(Date.now() \/ 1000))) || \"\");\n  } catch (e) { r = \"\"; }\n  if (!r) {\n    msg = \"❌ init 无响应（官方IP服务器不可达），当前源站未变更\";\n  } else if (r.charAt(0) !== \"{\") {\n    var reason = r.indexOf(\"SocketTimeout\") >= 0 ? \"超时\"\n               : r.indexOf(\"UnknownHost\") >= 0 ? \"域名解析失败\"\n               : r.indexOf(\"ConnectException\") >= 0 ? \"连接被拒\"\n               : \"网络错误\";\n    msg = \"❌ init \" + reason + \"，当前源站未变更\";\n  } else {\n    try {\n      var jo = JSON.parse(r);\n      var ips = (jo && jo.addresses) || [];\n      if (ips.length) {\n        var pool = ips.join(\",\");\n        SRC.put(\"picab_official_ips\", pool);\n        var st = picac_currentStation(SRC);\n        SRC.put(\"picab_pool\", picac_buildPool(st, pool));\n        try { SRC.refreshExplore(); } catch (e2) {}\n        msg = \"✅ 源站更新完成: \" + pool + \"（当前线路 \" + picac_stationName(st) + \"）\";\n      } else {\n        msg = \"⚠️ init 未返回 IP，当前源站未变更\";\n      }\n    } catch (e3) { msg = \"❌ init 返回格式异常，当前源站未变更\"; }\n  }\n  picac_say(jv, msg);\n}\n\n\/* 选源站：0=官方init 1=Cloudflare 2=系统DNS直连 *\/\nfunction PICAC_selectStation(jv, SRC, idx) {\n  idx = String(idx);\n  try {\n    SRC.put(\"picab_station\", idx);\n    SRC.put(\"picab_pool\", picac_buildPool(idx, picac_official(SRC)));\n    try { SRC.refreshExplore(); } catch (e2) {}\n    var pool = picac_pool(SRC);\n    picac_say(jv, \"✅ 已切换源站: \" + picac_stationName(idx)\n                 + (pool ? \"（池 \" + pool.split(\",\").length + \" 个IP）\" : \"（走系统DNS）\"));\n  } catch (e) {\n    picac_say(jv, \"切换失败: \" + e);\n  }\n}\n\nfunction PICAC_showStation(jv, SRC) {\n  var st = picac_currentStation(SRC);\n  var pool = picac_pool(SRC);\n  var off = picac_official(SRC);\n  var mode = picac_coverMode(SRC);\n  var mname = ({ \"ip\": \"IP直连(Host头)\", \"dns\": \"域名直连\", \"mirror\": \"镜像站\" })[mode] || mode;\n  var msg = \"当前源站: \" + picac_stationName(st)\n          + \"\\nIP池: \" + (pool ? pool : \"（系统DNS，不设dnsIp）\")\n          + \"\\n官方init: \" + (off ? off : \"（未更新）\")\n          + \"\\n封面模式: \" + mname;\n  picac_say(jv, msg);\n}\n\n\/* 封面模式：ip \/ dns \/ mirror *\/\nfunction PICAC_setCoverMode(jv, SRC, mode) {\n  mode = String(mode);\n  try {\n    if (mode !== \"ip\" && mode !== \"dns\" && mode !== \"mirror\") {\n      picac_say(jv, \"❌ 未知封面模式: \" + mode);\n      return;\n    }\n    SRC.put(\"picab_cover_mode\", mode);\n    var mname = ({ \"ip\": \"IP直连(Host头)\", \"dns\": \"域名直连\", \"mirror\": \"镜像站\" })[mode];\n    picac_say(jv, \"✅ 封面模式已切换: \" + mname + \"（重新搜索\/刷新详情后生效）\");\n  } catch (e) {\n    picac_say(jv, \"切换失败: \" + e);\n  }\n}\n\nfunction PICAC_setSort(jv, SRC, idx) {\n  try { SRC.put(\"picaSort\", String(idx)); } catch (e0) {}\n  var names = { \"0\": \"最新发布\", \"1\": \"最早发布\", \"2\": \"最多爱心\", \"3\": \"最多观看\" };\n  picac_say(jv, \"✅ 分类排序已切换: \" + names[String(idx)]);\n}\n\nfunction PICAC_setTC(jv, SRC, on) {\n  try { SRC.put(\"picaTC\", on ? \"1\" : \"0\"); } catch (e0) {}\n  picac_say(jv, on ? \"✅ 分类\/标签显示已切换: 简体\" : \"✅ 分类\/标签显示已切换: 繁体\");\n}\n\nfunction PICAC_diag(jv, SRC) {\n  var lines = [];\n  try {\n    var t0 = Date.now();\n    var r = String(jv.ajax(picac_opt(\"categories\", { method: \"GET\" }, SRC, jv)) || \"\");\n    var ok1 = r.indexOf('\"code\"') >= 0 && r.indexOf(\"categories\") >= 0 && r.indexOf(\"200\") >= 0;\n    lines.push(\"1.API签名+IP: \" + (ok1 ? \"✅\" : \"❌\") + \" \" + (Date.now() - t0) + \"ms\" + (ok1 ? \"\" : \" \" + r.substring(0, 40).replace(\/\\n\/g, \" \")));\n  } catch (e) { lines.push(\"1.API签名+IP: ❌ \" + String(e).substring(0, 40)); }\n  var pool = picac_pool(SRC);\n  lines.push(\"2.当前源站: \" + picac_stationName(picac_currentStation(SRC)) + \" 池: \" + (pool ? pool.substring(0, 46) : \"系统DNS\"));\n  try {\n    var t1 = Date.now();\n    var cov = picac_cover({ fileServer: \"https:\/\/storage-b.picacomic.com\",\n      path: \"tobeimg\/NLrAacfzHYyka85InF9J3kEyr5-kShySh5Ztk5HbIuA\/rs:fill:300:400:0\/g:sm\/aHR0cHM6Ly9zdG9yYWdlLWIucGljYWNvbWljLmNvbS9zdGF0aWMvc3ViX3N0b3JhZ2VfMS9kNC83Mi9kNDcyYmRhYS1iZGFlLTRjNDctYmFlNS03NDM3NmZlNDI3MTIuanBn.jpg\" }, SRC);\n    var r1 = String(jv.ajax(cov) || \"\");\n    lines.push(\"3.封面(\" + picac_coverMode(SRC) + \"): \" + (r1.length > 10000 ? \"✅\" : \"❌\") + \" \" + (Date.now() - t1) + \"ms \" + cov.substring(0, 60));\n  } catch (e) { lines.push(\"3.封面: ❌ \" + String(e).substring(0, 40)); }\n  try {\n    var t2 = Date.now();\n    var u2 = \"https:\/\/storage-b.picacomic.com\/static\/sub_storage_1\/b4\/b5\/b4b5042d-4a5c-4cbb-8a2c-13060b05b990.jpg\";\n    var r2 = String(jv.ajax(u2 + (pool ? ',{\"method\":\"GET\",\"timeout\":15000,\"dnsIp\":\"' + pool + '\"}' : \"\")) || \"\");\n    lines.push(\"4.正文图(带IP): \" + (r2.length > 10000 ? \"✅\" : \"❌\") + \" \" + (Date.now() - t2) + \"ms\");\n  } catch (e) { lines.push(\"4.正文图(带IP): ❌ 超时\"); }\n  var msg = lines.join(\"\\n\");\n  try { jv.upLoginData({ \"登录状态\": msg }); } catch (e1) {}\n  try { jv.toast(msg); } catch (e2) {}\n  try { jv.longToast(msg); } catch (e3) {}\n}\n","lastUpdateTime":"1792853819944","loginUi":"[{\"name\":\"账号\",\"type\":\"text\"},{\"name\":\"密码\",\"type\":\"password\"},{\"name\":\"🔑登录\",\"type\":\"button\",\"action\":\"login()\",\"style\":{\"layout_flexGrow\":1,\"layout_flexBasisPercent\":1}},{\"name\":\"①检查登录状态\",\"type\":\"button\",\"action\":\"PICAC_checkLogin(java, source)\",\"style\":{\"layout_flexGrow\":1,\"layout_flexBasisPercent\":0.45}},{\"name\":\"②每日签到\",\"type\":\"button\",\"action\":\"PICAC_punch(java, source)\",\"style\":{\"layout_flexGrow\":1,\"layout_flexBasisPercent\":0.45}},{\"name\":\"③退出登录\",\"type\":\"button\",\"action\":\"PICAC_logout(java, source)\",\"style\":{\"layout_flexGrow\":1,\"layout_flexBasisPercent\":0.45}},{\"name\":\"④刷新发现页\",\"type\":\"button\",\"action\":\"PICAC_refreshExplore(java, source)\",\"style\":{\"layout_flexGrow\":1,\"layout_flexBasisPercent\":0.45}},{\"name\":\"分类排序\",\"type\":\"text\"},{\"name\":\"最新\",\"type\":\"button\",\"action\":\"PICAC_setSort(java, source, 0)\",\"style\":{\"layout_flexGrow\":1,\"layout_flexBasisPercent\":0.25}},{\"name\":\"最早\",\"type\":\"button\",\"action\":\"PICAC_setSort(java, source, 1)\",\"style\":{\"layout_flexGrow\":1,\"layout_flexBasisPercent\":0.25}},{\"name\":\"爱心\",\"type\":\"button\",\"action\":\"PICAC_setSort(java, source, 2)\",\"style\":{\"layout_flexGrow\":1,\"layout_flexBasisPercent\":0.25}},{\"name\":\"观看\",\"type\":\"button\",\"action\":\"PICAC_setSort(java, source, 3)\",\"style\":{\"layout_flexGrow\":1,\"layout_flexBasisPercent\":0.25}},{\"name\":\"简繁显示\",\"type\":\"text\"},{\"name\":\"简体\",\"type\":\"button\",\"action\":\"PICAC_setTC(java, source, true)\",\"style\":{\"layout_flexGrow\":1,\"layout_flexBasisPercent\":0.5}},{\"name\":\"繁体\",\"type\":\"button\",\"action\":\"PICAC_setTC(java, source, false)\",\"style\":{\"layout_flexGrow\":1,\"layout_flexBasisPercent\":0.5}},{\"name\":\"源 站 管 理\",\"type\":\"text\"},{\"name\":\"🔄源站更新\",\"type\":\"button\",\"action\":\"PICAC_updateStation(java, source)\",\"style\":{\"layout_flexGrow\":1,\"layout_flexBasisPercent\":0.45}},{\"name\":\"📋查看当前\",\"type\":\"button\",\"action\":\"PICAC_showStation(java, source)\",\"style\":{\"layout_flexGrow\":1,\"layout_flexBasisPercent\":0.45}},{\"name\":\"①官方init\",\"type\":\"button\",\"action\":\"PICAC_selectStation(java, source, 0)\",\"style\":{\"layout_flexGrow\":1,\"layout_flexBasisPercent\":0.33}},{\"name\":\"②Cloudflare\",\"type\":\"button\",\"action\":\"PICAC_selectStation(java, source, 1)\",\"style\":{\"layout_flexGrow\":1,\"layout_flexBasisPercent\":0.34}},{\"name\":\"③系统DNS\",\"type\":\"button\",\"action\":\"PICAC_selectStation(java, source, 2)\",\"style\":{\"layout_flexGrow\":1,\"layout_flexBasisPercent\":0.33}},{\"name\":\"封 面 模 式\",\"type\":\"text\"},{\"name\":\"🚀IP直连\",\"type\":\"button\",\"action\":\"PICAC_setCoverMode(java, source, 'ip')\",\"style\":{\"layout_flexGrow\":1,\"layout_flexBasisPercent\":0.33}},{\"name\":\"🌐域名直连\",\"type\":\"button\",\"action\":\"PICAC_setCoverMode(java, source, 'dns')\",\"style\":{\"layout_flexGrow\":1,\"layout_flexBasisPercent\":0.34}},{\"name\":\"🪞镜像站\",\"type\":\"button\",\"action\":\"PICAC_setCoverMode(java, source, 'mirror')\",\"style\":{\"layout_flexGrow\":1,\"layout_flexBasisPercent\":0.33}},{\"name\":\"🔍一键诊断\",\"type\":\"button\",\"action\":\"PICAC_diag(java, source)\",\"style\":{\"layout_flexGrow\":1,\"layout_flexBasisPercent\":1}},{\"name\":\"登录状态\",\"type\":\"text\"}]","loginUrl":"\/\/ 登录：只有 loginUrl 的 login() 绑定 result（= 登录面板各输入）\nfunction login() {\n  var u = String(result[\"账号\"] || \"\").replace(\/^\\s+|\\s+$\/g, \"\");\n  var p = String(result[\"密码\"] || \"\").replace(\/^\\s+|\\s+$\/g, \"\");\n  if (!u) { java.longToast(\"请填写账号\"); return; }\n  if (!p) { java.longToast(\"请填写密码\"); return; }\n  var msg = \"\";\n  try {\n    var path = \"auth\/sign-in\";\n    var h = picac_headers(path, \"POST\", source, java);\n    h[\"Content-Type\"] = \"application\/json; charset=UTF-8\";\n    var opt = { method: \"POST\", body: JSON.stringify({ email: u, password: p }),\n                headers: h, timeout: 30000, retry: 2 };\n    var dp = picac_pool(source);\n    if (dp) { opt.dnsIp = dp; }\n    var resp = java.ajax(\"https:\/\/picaapi.picacomic.com\/\" + path + \",\" + JSON.stringify(opt));\n    var txt = String(resp || \"\");\n    var jo = null;\n    try { jo = JSON.parse(txt); } catch (e0) {}\n    if (jo && jo.code === 200 && jo.data && jo.data.token) {\n      source.putLoginHeader(jo.data.token);\n      try { source.put(\"picab_token\", jo.data.token); } catch (e5) {}\n      try { source.refreshExplore(); } catch (e6) {}\n      msg = \"✅ 登录成功: \" + u;\n    } else {\n      msg = \"❌ 登录失败: \" + (jo && jo.message ? jo.message : txt.substring(0, 80));\n    }\n  } catch (e) { msg = \"登录异常: \" + e; }\n  try { java.upLoginData({ \"登录状态\": msg }); } catch (e1) {}\n  try { java.toast(msg); } catch (e2) {}\n  try { java.longToast(msg); } catch (e3) {}\n}\n","respondTime":180000,"ruleBookInfo":{"author":"author","coverUrl":"cover","init":"<js>\nvar _id = picac_bookid(baseUrl);\nif (!_id) { throw new Error(\"invalid baseUrl: \" + baseUrl); }\nvar _j = JSON.parse(picac_get(\"comics\/\" + _id, source, java));\nif (_j.code !== 200 || !_j.data || !_j.data.comic) {\n  throw new Error(\"详情获取失败: \" + (_j.message || _j.code));\n}\nvar _c = _j.data.comic;\nvar _it = {\n  name: _c.title || \"\",\n  author: _c.author || \"\",\n  cover: picac_cover(_c.thumb, source),\n  intro: _c.description || \"\",\n  kind: [String(_c.categories || []).replace(\/,\/g, \" \"),\n         String(_c.tags || []).replace(\/,\/g, \" \"),\n         _c.chineseTeam ? \"汉化组: \" + _c.chineseTeam : \"\",\n         _c.finished ? \"完结\" : \"连载\",\n         _c.viewsCount ? _c.viewsCount + \"次观看\" : \"\",\n         _c.likesCount ? _c.likesCount + \"人喜欢\" : \"\"].filter(Boolean).join(\"\\n\"),\n  tocUrl: picac_opt(\"comics\/\" + _id + \"\/eps?page=1\", { method: \"GET\" }, source, java),\n  lastChapter: \"\",\n  wordCount: (_c.pagesCount || \"\") + \"P\"\n};\nbook.putVariable(\"picab_bookid\", _id);\nJSON.stringify(_it);\n<\/js>","intro":"intro","kind":"kind","lastChapter":"lastChapter","name":"name","tocUrl":"tocUrl","wordCount":"wordCount"},"ruleContent":{"content":"<js>\nvar _j = JSON.parse(result);\nif (_j.code !== 200) { throw new Error(\"正文获取失败: \" + (_j.message || _j.code)); }\nvar _d = (_j.data && _j.data.pages) ? (_j.data.pages.docs || []) : [];\nvar _contentOut = \"\";\nif (!_d.length) {\n  java.log(\"哔咔正文为空: \" + String(result).substring(0, 200));\n  _contentOut = \"<p>正文获取失败：本书请长按→删除→重新搜索加入。<\/p>\";\n} else {\nvar _poolBase = [\n  \"172.67.194.19\",\"172.67.80.1\",\"104.21.235.3\",\"104.21.235.4\",\"104.21.91.145\",\n  \"104.19.53.76\",\"104.21.80.1\",\"104.21.81.1\",\"104.16.0.1\",\"104.17.0.1\"\n];\n\/* 当前源站 IP 池优先；官方 init IP 追加在前 *\/\ntry {\n  var _manual = String(source.get(\"picab_pool\") || \"\");\n  if (_manual === \"none\") { _manual = \"\"; }\n  else if (!_manual) {\n    try { _manual = String(source.get(\"picab_official_ips\") || \"\"); } catch (e1) {}\n  }\n  if (_manual) {\n    _manual.split(\",\").forEach(function(ip) {\n      ip = ip.trim(); if (ip && _poolBase.indexOf(ip) < 0) _poolBase.unshift(ip);\n    });\n  }\n} catch(e0) {}\n\/* 图片 URL 由 picac_img 统一生成（IP+Host 头直连），\n   不再在外层追加 dnsIp —— 漫画浏览模式走 Glide.getGlideUrl() 会丢弃 dnsIp，\n   只有 URL 里自带 IP + Host 头才能在两种模式下都加载成功。 *\/\nvar _out = _d.map(function (x, idx) {\n  var u = picac_img(x.media, source);\n  return '<img src=\"' + u + '\">';\n});\nif (_out.length) {\n  java.log(\"PICADIAG v84 图数=\" + _out.length + \" 池数=\" + _poolBase.length + \" 首src=\" + _out[0].substring(9, 260));\n}\nvar _diag = \"<p style=\\\"font-size:11px\\\">[诊断] 图数:\" + _out.length + \" 池:\" + _poolBase.length + \" IP轮换:开<\/p>\";\n_contentOut = _out.join(\"\\n\") + \"\\n\\u200b\" + String(Math.floor(Date.now() \/ 60000)) + _diag;\n}\n_contentOut;\n<\/js>","imageStyle":"FULL","nextContentUrl":"<js>\nvar _j = JSON.parse(result);\nvar _p = (_j.data && _j.data.pages) ? _j.data.pages : null;\nvar _next = \"javascript:void(0)\";\nif (_p) {\n  var _cur = (_p.page === null || _p.page === undefined) ? 1 : _p.page;\n  var _total = (_p.pages === null || _p.pages === undefined) ? 1 : _p.pages;\n  if (_total > 1 && _cur < _total) {\n    var _ord = String(baseUrl || \"\").match(new RegExp(\"order\/(\\\\d+)\"));\n    if (_ord) {\n      var _bid = picac_bookid(baseUrl);\n      var _at = String.fromCharCode(64,106,115,58);\n      var _sg = '(function(){ var K=\"C69BAF41DA5ABD1FFEDC6D2FEA56B\"; var S=\"~d}$Q7$eIni=V)9\"+String.fromCharCode(92)+\"RK\/P.RM4;9[7|@\/CA}b~OW!3?EV\"+String.fromCharCode(96,58,60,62)+\"M7pddUBL5n|0\/*Cn\"; var PL=\"\"; try{PL=String(source.get(\"picab_pool\")||\"\");}catch(e0){} if(PL===\"none\"){PL=\"\";}else if(!PL){PL=\"172.67.194.19,172.67.80.1,104.21.235.3,104.21.235.4,104.21.91.145,104.19.53.76,104.21.80.1,104.21.81.1,104.16.0.1,104.17.0.1\";} var pre=String(result); var a=pre.substring(pre.indexOf(\":\/\/\")+3); var p=a.substring(a.indexOf(\"\/\")); if(p.charAt(0)===\"\/\"){p=p.substring(1);} var ts=String(Math.floor(Date.now()\/1000)); var nc=\"\"; for(var i=0;i<32;i++)nc+=\"0123456789abcdef\".charAt(Math.floor(Math.random()*16)); var raw=(p+ts+nc+\"GET\"+K).toLowerCase(); var m=Packages.javax.crypto.Mac.getInstance(\"HmacSHA256\"); m.init(new Packages.javax.crypto.spec.SecretKeySpec(new Packages.java.lang.String(S).getBytes(\"UTF-8\"),\"HmacSHA256\")); var b=m.doFinal(new Packages.java.lang.String(raw).getBytes(\"UTF-8\")); var sg=\"\"; for(var i=0;i<b.length;i++){var x=b[i]&0xff;sg+=(x<16?\"0\":\"\")+x.toString(16);} var h={\"User-Agent\":\"okhttp\/3.8.1\",\"accept\":\"application\/vnd.picacomic.com.v1+json\",\"api-key\":K,\"app-channel\":\"2\",\"app-version\":\"2.2.1.2.3.3\",\"app-uuid\":\"defaultUuid\",\"app-platform\":\"android\",\"app-build-version\":\"44\",\"image-quality\":\"original\",\"time\":ts,\"nonce\":nc,\"signature\":sg}; var au=\"\"; try{au=String(source.get(\"picab_token\"));}catch(e){} if(au&&au!==\"null\"&&au!==\"\"){h[\"authorization\"]=au;} var opt={method:\"GET\",timeout:25000,retry:0,headers:h}; if(PL){opt.dnsIp=PL;} return \"https:\/\/picaapi.picacomic.com\/\"+p+\",\"+JSON.stringify(opt); })()';\n      _next = \"https:\/\/picaapi.picacomic.com\/comics\/\" + _bid + \"\/order\/\" + _ord[1] + \"\/pages?page=\" + (_cur + 1) + _at + _sg;\n    }\n  }\n}\n_next;\n<\/js>"},"ruleExplore":{"author":"author","bookList":"<js>\n\/\/ ============ 发现页解析（完全内联，不调用 jsLib） ============\nvar __API_KEY = \"C69BAF41DA5ABD1FFEDC6D2FEA56B\";\nvar __SECRET = \"~d}$Q7$eIni=V)9\" + String.fromCharCode(92) + \"RK\/P.RM4;9[7|@\/CA}b~OW!3?EV\" + String.fromCharCode(96, 58, 60, 62) + \"M7pddUBL5n|0\/*Cn\";\nvar __BASE = \"https:\/\/picaapi.picacomic.com\/\";\nvar __POOL = [\"172.67.194.19\", \"172.67.80.1\", \"104.21.235.3\",\n              \"104.21.235.4\", \"104.21.91.145\", \"104.19.53.76\",\n              \"104.21.80.1\", \"104.21.81.1\", \"104.16.0.1\", \"104.17.0.1\"];\nfunction __coverParse(media) {\n  var out = { host: \"\", path: \"\", raw: \"\", isSB: false };\n  if (!media) { return out; }\n  var fs = String(media.fileServer || \"\").replace(\/\\\/$\/, \"\");\n  var path = String(media.path || \"\").replace(\/^\\\/?\/, \"\");\n  out.raw = path;\n  if (!path) { return out; }\n  if (path.indexOf(\"tobeimg\/\") === 0) {\n    var idx = path.indexOf(\"\/g:sm\/\");\n    if (idx >= 0) {\n      var b64 = path.substring(idx + 6);\n      var m = b64.match(\/^([A-Za-z0-9+\/=]+)(\\.[A-Za-z0-9]+)?$\/);\n      if (m) {\n        try {\n          var pay = m[1];\n          while (pay.length % 4 !== 0) { pay += \"=\"; }\n          var real = String(new Packages.java.lang.String(\n              Packages.android.util.Base64.decode(pay, 0), \"UTF-8\"));\n          var i2 = real.indexOf(\"\/static\/\");\n          if (i2 >= 0) {\n            out.host = real.substring(0, i2).replace(\/^https?:\\\/\\\/\/, \"\");\n            out.path = real.substring(i2);\n            out.isSB = out.host.indexOf(\"storage-b\") >= 0;\n            return out;\n          }\n        } catch (e0) {}\n      }\n    }\n  }\n  out.host = fs.replace(\/^https?:\\\/\\\/\/, \"\");\n  if (path.indexOf(\"tobs\/\") === 0) { path = path.substring(5); }\n  out.path = \"\/static\/\" + path;\n  out.isSB = out.host.indexOf(\"storage-b\") >= 0;\n  return out;\n}\nfunction __hash(s) {\n  var h = 0; s = String(s || \"\");\n  for (var i = 0; i < s.length; i++) { h = ((h << 5) - h + s.charCodeAt(i)) | 0; }\n  return Math.abs(h);\n}\nfunction __pool() {\n  var p = \"\";\n  try { p = String(source.get(\"picab_pool\") || \"\"); } catch (e0) {}\n  if (p === \"none\") { return \"\"; }\n  if (p) { return p; }\n  return __POOL.join(\",\");\n}\nfunction __coverMode() {\n  try {\n    var m = String(source.get(\"picab_cover_mode\") || \"ip\");\n    return (m === \"dns\" || m === \"mirror\") ? m : \"ip\";\n  } catch (e0) { return \"ip\"; }\n}\nfunction __cover(media) {\n  var t = __coverParse(media);\n  if (!t.raw) { return \"\"; }\n  var mode = __coverMode();\n  if (mode === \"mirror\") { return \"https:\/\/s3.go2778.com\/static\/\" + t.raw; }\n  if (!t.host) { return \"\"; }\n  if (mode === \"dns\") { return \"https:\/\/\" + t.host + t.path; }\n  if (t.isSB) {\n    var ips = __POOL;\n    var ip = ips[__hash(t.path) % ips.length];\n    return \"http:\/\/\" + ip + t.path\n         + ',{\"method\":\"GET\",\"timeout\":20000,\"headers\":{\"Host\":\"' + t.host + '\"}}';\n  }\n  return \"https:\/\/\" + t.host + t.path;\n}\n\nvar __token = \"\";\ntry {\n  var __lh = String(source.getLoginHeader() || \"\");\n  if (__lh && __lh !== \"null\") {\n    var __m = __lh.match(new RegExp('\"authorization\"\\\\s*:\\\\s*\"([^\"]+)\"'));\n    __token = __m ? __m[1] : __lh;\n  }\n} catch (eT0) {}\nif (!__token) {\n  try { __token = String(source.get(\"picab_token\") || \"\"); } catch (eT1) {}\n}\n\nfunction __hmac(raw, key) {\n  var m = Packages.javax.crypto.Mac.getInstance(\"HmacSHA256\");\n  m.init(new Packages.javax.crypto.spec.SecretKeySpec(\n      new Packages.java.lang.String(key).getBytes(\"UTF-8\"), \"HmacSHA256\"));\n  var b = m.doFinal(new Packages.java.lang.String(raw).getBytes(\"UTF-8\"));\n  var s = \"\";\n  for (var i = 0; i < b.length; i++) {\n    var x = b[i] & 0xff;\n    s += (x < 16 ? \"0\" : \"\") + x.toString(16);\n  }\n  return s;\n}\n\nfunction __hdr(path, method) {\n  var ts = String(Math.floor(Date.now() \/ 1000));\n  var nc = \"\";\n  for (var i = 0; i < 32; i++) {\n    nc += \"0123456789abcdef\".charAt(Math.floor(Math.random() * 16));\n  }\n  var raw = (path + ts + nc + method + __API_KEY).toLowerCase();\n  var h = {\n    \"User-Agent\": \"okhttp\/3.8.1\",\n    \"accept\": \"application\/vnd.picacomic.com.v1+json\",\n    \"api-key\": __API_KEY,\n    \"app-channel\": \"2\",\n    \"app-version\": \"2.2.1.2.3.3\",\n    \"app-uuid\": \"defaultUuid\",\n    \"app-platform\": \"android\",\n    \"app-build-version\": \"44\",\n    \"image-quality\": \"original\",\n    \"time\": ts,\n    \"nonce\": nc,\n    \"signature\": __hmac(raw, __SECRET)\n  };\n  if (__token && __token !== \"null\") { h[\"authorization\"] = __token; }\n  return h;\n}\n\nvar _j = JSON.parse(result);\nif (_j.code !== 200) { throw new Error(\"发现页失败: \" + (_j.message || _j.code)); }\n\nvar _list = [];\ntry {\n  if (String(baseUrl).indexOf(\"knight-leaderboard\") >= 0) {\n    var _us = _j.data.users || [];\n    _list = _us.map(function (u) {\n      var __t = \"\";\n      try {\n        __t = String(u.avatar ? (String(u.avatar.fileServer || \"\").replace(\/\\\/$\/, \"\") + \"\/static\/\" + String(u.avatar.path || \"\")) : \"\");\n      } catch (eA) {}\n      return {\n        name: java.t2s(String(u.name || \"\")),\n        author: \"Lv.\" + (u.level || \"?\") + \" · \" + (u.title || \"\"),\n        cover: __t,\n        intro: (u.slogan || \"\") + \"\\n[骑士档案]\",\n        kind: \"骑士榜\",\n        latest: \"\", word: \"\",\n        url: __BASE + \"comics?page=1&ca=\" + (u._id || \"\")\n            + ',{\"method\":\"GET\",\"retry\":3,\"timeout\":20000,'\n            + '\"headers\":' + JSON.stringify(__hdr(\"comics?page=1&ca=\" + (u._id || \"\"), \"GET\"))\n            + ',\"dnsIp\":\"' + __pool() + '\"}'\n      };\n    });\n  } else {\n    var _d = _j.data || {};\n    var __docs = [];\n    if (_d.collections) {\n      (_d.collections || []).forEach(function (g) {\n        var arr = (g.comics && g.comics.docs) ? g.comics.docs : (g.comics || []);\n        arr.forEach(function (c) { c.__group = g.title; __docs.push(c); });\n      });\n    } else if (_d.comics) {\n      __docs = _d.comics.docs || _d.comics || [];\n    } else if (_d.docs) {\n      __docs = _d.docs;\n    } else if (Array.isArray(_d)) {\n      __docs = _d;\n    }\n    if (!Array.isArray(__docs)) { __docs = []; }\n    _list = __docs.map(function (c) {\n      if (!c) { return null; }\n      return {\n        name: c.title || \"\",\n        author: c.author || \"\",\n        cover: __cover(c.thumb),\n        intro: \"\",\n        kind: [(c.__group || \"\"),\n               String(c.categories || []).replace(\/,\/g, \" \"),\n               c.finished ? \"完结\" : \"连载\",\n               c.likesCount ? c.likesCount + \"♥\" : \"\"].filter(Boolean).join(\" · \"),\n        latest: \"\", word: \"\",\n        url: __BASE + \"comics\/\" + (c._id || \"\")\n            + ',{\"method\":\"GET\",\"retry\":3,\"timeout\":20000,'\n            + '\"headers\":' + JSON.stringify(__hdr(\"comics\/\" + (c._id || \"\"), \"GET\"))\n            + ',\"dnsIp\":\"' + __pool() + '\"}'\n      };\n    });\n  }\n} catch (eItem) {\n  _list = [{\n    name: \"[ERR] \" + String(eItem).substring(0, 130),\n    author: \"诊断行\", cover: \"\", intro: \"\", kind: \"err\", latest: \"\", word: \"\", url: \"\"\n  }];\n}\nif (!_list.length) {\n  var _tail = String(baseUrl);\n  _tail = _tail.substring(_tail.lastIndexOf(\"\/\") + 1).split(\",\")[0].substring(0, 40);\n  _list = [{ name: \"[空] 此列表暂无内容 (\" + _tail + \")\",\n             author: \"接口返回 code=\" + _j.code + \" 但解析为空\",\n             cover: \"\", intro: \"换个分类试试，或稍后再来\", kind: \"空列表\", latest: \"\", word: \"\", url: \"\" }];\n}\n_list.filter(function (x) { return x; });\n<\/js>\n","bookUrl":"url","coverUrl":"cover","intro":"intro","kind":"kind","lastChapter":"latest","name":"name"},"ruleSearch":{"author":"author","bookList":"<js>\nvar _j = JSON.parse(result);\nif (_j.code !== 200) { throw new Error(\"搜索失败: \" + (_j.message || _j.code)); }\nvar _d = picac_list(_j.data);\n_d.map(function (x) { return picac_item(x, source, java); }).filter(function (x) { return x; });\n<\/js>","bookUrl":"url","coverUrl":"cover","intro":"intro","kind":"kind","lastChapter":"latest","name":"name"},"ruleToc":{"chapterList":"<js>\nvar _j = JSON.parse(result);\nif (_j.code !== 200) { throw new Error(\"目录获取失败: \" + (_j.message || _j.code)); }\nvar _e = (_j.data && _j.data.eps) ? (_j.data.eps.docs || []) : [];\nvar _bid = picac_bookid(baseUrl);\nvar _at = String.fromCharCode(64,106,115,58);\nvar _sg = '(function(){ var K=\"C69BAF41DA5ABD1FFEDC6D2FEA56B\"; var S=\"~d}$Q7$eIni=V)9\"+String.fromCharCode(92)+\"RK\/P.RM4;9[7|@\/CA}b~OW!3?EV\"+String.fromCharCode(96,58,60,62)+\"M7pddUBL5n|0\/*Cn\"; var PL=\"\"; try{PL=String(source.get(\"picab_pool\")||\"\");}catch(e0){} if(PL===\"none\"){PL=\"\";}else if(!PL){PL=\"172.67.194.19,172.67.80.1,104.21.235.3,104.21.235.4,104.21.91.145,104.19.53.76,104.21.80.1,104.21.81.1,104.16.0.1,104.17.0.1\";} var pre=String(result); var a=pre.substring(pre.indexOf(\":\/\/\")+3); var p=a.substring(a.indexOf(\"\/\")); if(p.charAt(0)===\"\/\"){p=p.substring(1);} var ts=String(Math.floor(Date.now()\/1000)); var nc=\"\"; for(var i=0;i<32;i++)nc+=\"0123456789abcdef\".charAt(Math.floor(Math.random()*16)); var raw=(p+ts+nc+\"GET\"+K).toLowerCase(); var m=Packages.javax.crypto.Mac.getInstance(\"HmacSHA256\"); m.init(new Packages.javax.crypto.spec.SecretKeySpec(new Packages.java.lang.String(S).getBytes(\"UTF-8\"),\"HmacSHA256\")); var b=m.doFinal(new Packages.java.lang.String(raw).getBytes(\"UTF-8\")); var sg=\"\"; for(var i=0;i<b.length;i++){var x=b[i]&0xff;sg+=(x<16?\"0\":\"\")+x.toString(16);} var h={\"User-Agent\":\"okhttp\/3.8.1\",\"accept\":\"application\/vnd.picacomic.com.v1+json\",\"api-key\":K,\"app-channel\":\"2\",\"app-version\":\"2.2.1.2.3.3\",\"app-uuid\":\"defaultUuid\",\"app-platform\":\"android\",\"app-build-version\":\"44\",\"image-quality\":\"original\",\"time\":ts,\"nonce\":nc,\"signature\":sg}; var au=\"\"; try{au=String(source.get(\"picab_token\"));}catch(e){} if(au&&au!==\"null\"&&au!==\"\"){h[\"authorization\"]=au;} var opt={method:\"GET\",timeout:25000,retry:0,headers:h}; if(PL){opt.dnsIp=PL;} return \"https:\/\/picaapi.picacomic.com\/\"+p+\",\"+JSON.stringify(opt); })()';\nvar _out = _e.map(function (x) {\n  return {\n    title: x.title || (\"第\" + x.order + \" 话\"),\n    url: \"https:\/\/picaapi.picacomic.com\/comics\/\" + _bid + \"\/order\/\" + x.order + \"\/pages?page=1\" + _at + _sg\n  };\n});\n_out;\n<\/js>","chapterName":"title","chapterUrl":"url"},"searchUrl":"<js>\npicac_opt(\"comics\/advanced-search?page=\" + (page || 1),\n        { method: \"POST\", body: JSON.stringify({ keyword: key, sort: \"dd\" }),\n          timeout: 60000, retry: 2 },\n        source, java);\n<\/js>","variableComment":"picaToken=登录后的 authorization（登录成功后自动写入，也可手动粘贴）","weight":0}]