[{"bookSourceComment":"版本: 20261026\n作者: 知秋\n反馈群：766236637\ntg群：https:\/\/t.me\/+dcNnFFZv5KtmYjIx\n支持全站小说，以及全站漫画\n速率限制为1分钟80章\n注意：请牢记你的密码，用户名为设备码\n\n登录：阅读内「注册\/登录同步」登录成功后从 Cookie CYJapiToken 写入源变量。\nJWT 过期时请求会提示并自动打开登录页，关闭后重新同步。\n账号信息 \/ 上游绑定 \/ 改密请在 Web 控制台完成。\n\n搜索前缀：\n- 无前缀：小说\n- c: \/ c：：漫画\n- g: \/ g：：共享搜索（share=true）；漫画共享用 c:g:关键词\n- 纯数字：书号 bid（当前类型）\n- a:\/v:\/p：其它类型（暂未开放）\n\n发现类型请用发现页自定义按钮切换；登录页可切男频\/女频与 API 地址。\n","bookSourceGroup":"cyj,api","bookSourceName":"✿次元姬（知秋）✿","bookSourceType":0,"bookSourceUrl":"https:\/\/cyj.vv9v.cn","concurrentRate":"1000","customButton":true,"customOrder":0,"enabled":true,"enabledCookieJar":true,"enabledExplore":true,"eventListener":true,"exploreUrl":"@js:\nvar full = { layout_flexGrow: 1, layout_flexBasisPercent: 1 }\nvar half = { layout_flexGrow: 1, layout_flexBasisPercent: 0.45 }\nsyncExplorePrefsFromMap.call(this)\nfunction exploreTypeLabel() {\n  var t = ''\n  try { t = String(source.get('exploreType') || source.get('type') || 'all') } catch (e) { }\n  if (t === 'novel') return '小说'\n  if (t === 'comic') return '漫画'\n  return '全部'\n}\nfunction genderLabel() {\n  try {\n    return String(source.get('gender') || '1') === '0' ? '女频' : '男频'\n  } catch (e) {\n    return '男频'\n  }\n}\nfunction styleLabel() {\n  try {\n    return String(source.get('exploreStyle') || 'default') === 'compact' ? '紧凑' : '默认'\n  } catch (e) {\n    return '默认'\n  }\n}\nvar compact = false\ntry { compact = String(source.get('exploreStyle') || '') === 'compact' } catch (eC) { }\nvar out = []\nout.push({\n  title: '✿ 发现设置 ✿',\n  type: 'button',\n  action: \"java.toast('切换下拉后会自动刷新分类')\",\n  style: full,\n})\nout.push({\n  title: '发现类型',\n  type: 'select',\n  chars: ['全部', '小说', '漫画'],\n  default: exploreTypeLabel(),\n  action: \"applyExploreTypeFromMap(String(infoMap.get().get('发现类型')||''))\",\n  style: half,\n})\nout.push({\n  title: '小说频道',\n  type: 'select',\n  chars: ['男频', '女频'],\n  default: genderLabel(),\n  action: \"applyExploreGenderFromMap(String(infoMap.get().get('小说频道')||''))\",\n  style: half,\n})\nout.push({\n  title: '发现样式',\n  type: 'select',\n  chars: ['默认', '紧凑'],\n  default: styleLabel(),\n  action: \"applyExploreStyleFromMap(String(infoMap.get().get('发现样式')||''))\",\n  style: half,\n})\nout.push({\n  title: '立即刷新发现',\n  type: 'button',\n  action: 'refreshExploreNow()',\n  style: half,\n})\nvar exploreType = String(source.get('exploreType') || source.get('type') || 'all')\nif (exploreType !== 'all' && exploreType !== 'novel' && exploreType !== 'comic') {\n  exploreType = 'all'\n}\ntry { source.put('type', exploreType) } catch (e) { }\nvar gender = String(source.get('gender') || '1')\nvar data = requestApi.call(this, '\/explore\/init', {\n  type: exploreType,\n  gender: gender,\n}) || []\nvar dense = { layout_flexGrow: 1, layout_flexBasisPercent: 0.2 }\nvar normalPct = 0.25\nfor (var i = 0; i < data.length; i++) {\n  var item = data[i]\n  if (!item || item.title == null) continue\n  var hasUrl = !!(item.url && String(item.url).trim())\n  if (hasUrl) {\n    var st = item.style || {}\n    var pct = normalPct\n    try {\n      if (st.layout_flexBasisPercent != null) pct = Number(st.layout_flexBasisPercent)\n    } catch (eP) { }\n    if (compact) {\n      out.push({\n        title: item.title,\n        url: item.url,\n        style: dense,\n      })\n    } else {\n      out.push({\n        title: item.title,\n        url: item.url,\n        style: {\n          layout_flexGrow: 1,\n          layout_flexBasisPercent: pct > 0 && pct < 1 ? pct : normalPct,\n        },\n      })\n    }\n  } else {\n    out.push(item)\n  }\n}\nJSON.stringify(out)\n","jsLib":"var DEFAULT_API = 'https:\/\/cyj.vv9v.cn'\nvar COOKIE_TOKEN_KEY = 'CYJapiToken'\nfunction bookSourceOf(self) {\n  return (self && self.source) || (typeof source !== 'undefined' ? source : null)\n}\nfunction javaOf(self) {\n  return (self && self.java) || (typeof java !== 'undefined' ? java : null)\n}\nfunction getApiBase() {\n  var bs = bookSourceOf(this)\n  try {\n    var h = String((bs && bs.get('apiHost')) || '').trim()\n    if (h) return h.replace(\/\\\/$\/, '')\n  } catch (e) { }\n  try {\n    var info = JSON.parse(String((bs && bs.getLoginInfo()) || '{}'))\n    var fromLogin = String(info['API地址'] || info['apiHost'] || '').trim()\n    if (fromLogin) return fromLogin.replace(\/\\\/$\/, '')\n  } catch (e2) { }\n  return DEFAULT_API\n}\nvar api = DEFAULT_API\nfunction refreshApi() {\n  api = getApiBase.call(this)\n  return api\n}\nfunction getJwt() {\n  try {\n    var bs = bookSourceOf(this)\n    return String((bs && bs.getVariable()) || '').trim()\n  } catch (e) {\n    return ''\n  }\n}\nfunction asBook(r) {\n  if (r == null) return {}\n  if (typeof r === 'string') {\n    try { r = JSON.parse(r) } catch (e) { return {} }\n  }\n  try {\n    if (r && r.data != null && typeof r.data === 'object' && !Array.isArray(r.data)\n      && (r.code != null || r.msg != null)\n      && r.bid == null && r.type == null && r.name == null) {\n      r = r.data\n    }\n  } catch (e2) { }\n  return r || {}\n}\nfunction loginMap(k) {\n  var bs = bookSourceOf(this)\n  if (!this._mapCache) {\n    try { this._mapCache = bs ? bs.getLoginInfoMap() : null } catch (e) { this._mapCache = null }\n  }\n  var m = this._mapCache\n  if (k == null) return m\n  if (!m) {\n    try {\n      var info = JSON.parse(String((bs && bs.getLoginInfo()) || '{}'))\n      var v = info[k]\n      return v == null ? '' : String(v)\n    } catch (e2) { return '' }\n  }\n  var v2 = m.get ? m.get(k) : m[k]\n  return v2 == null ? '' : String(v2)\n}\nfunction headerGet(headers, name) {\n  if (!headers) return ''\n  var want = String(name || '').toLowerCase()\n  try {\n    if (typeof headers.get === 'function') {\n      var a = headers.get(name)\n      if (a) return String(a)\n      var b = headers.get(want)\n      if (b) return String(b)\n    }\n  } catch (e0) { }\n  try {\n    if (typeof headers.get === 'function') {\n      var names = headers.names ? headers.names() : null\n      if (names) {\n        var it = names.iterator ? names.iterator() : null\n        if (it) {\n          while (it.hasNext()) {\n            var n = String(it.next())\n            if (n.toLowerCase() === want) {\n              var hv = headers.get(n)\n              if (hv) return String(hv)\n            }\n          }\n        }\n      }\n    }\n  } catch (e1) { }\n  try {\n    var keys = Object.keys(headers)\n    for (var i = 0; i < keys.length; i++) {\n      if (String(keys[i]).toLowerCase() === want) return String(headers[keys[i]] || '')\n    }\n  } catch (e2) { }\n  return ''\n}\nfunction getApiUrl(path, data) {\n  refreshApi.call(this)\n  var token = getJwt.call(this)\n  data = data || {}\n  var qs = Object.entries(data)\n    .filter(function (kv) { return kv[1] !== undefined && kv[1] !== null && kv[1] !== '' })\n    .map(function (v) { return encodeURIComponent(v[0]) + '=' + encodeURIComponent(String(v[1])) })\n    .join('&')\n  var option = {\n    method: 'GET',\n    webView: false,\n    headers: {\n      Authorization: 'Bearer ' + token,\n    },\n  }\n  return api + path + (qs ? ('?' + qs) : '') + ',' + JSON.stringify(option)\n}\nfunction requestApi(path, data) {\n  var url = getApiUrl.call(this, path, data || {})\n  var javaObj = javaOf(this)\n  if (!javaObj) return null\n  var requestId = '(none)'\n  try {\n    var resp = javaObj.connect(url)\n    try {\n      var hid = headerGet(resp.headers(), 'X-Request-Id')\n      if (hid) requestId = hid\n    } catch (eH) { }\n    var httpCode = 0\n    try { httpCode = Number(resp.code()) } catch (eC) { }\n    var raw = String(resp.body() || '')\n    if (httpCode === 401 || httpCode === 403) {\n      javaObj.log('请求 ' + path + ' HTTP ' + httpCode + ' requestId=' + requestId)\n      javaObj.longToast('\\n未登录或无权限 (HTTP ' + httpCode + ')\\n请先在登录页「登录同步」\\nrequestId: ' + requestId)\n      return null\n    }\n    var res = {}\n    try { res = JSON.parse(raw) } catch (eP) {\n      javaObj.log('请求 ' + path + ' 非 JSON：' + raw.slice(0, 200))\n      javaObj.longToast('\\n请求失败\\nrequestId: ' + requestId + '\\n响应不是 JSON')\n      return null\n    }\n    if (res.requestId) requestId = String(res.requestId)\n    var ok = (res.msg == 'success' || res.msg === 'success') && (res.code == null || Number(res.code) >= 0)\n    if (!ok) {\n      var msg = String(res.msg || 'error')\n      javaObj.log('请求失败 ' + path + ' code=' + res.code + ' msg=' + msg + ' requestId=' + requestId)\n      javaObj.longToast('\\n操作失败 code=' + res.code + '\\n' + msg + '\\nrequestId: ' + requestId)\n      return null\n    }\n    if (res.data !== undefined) return res.data\n    return res\n  } catch (e) {\n    try {\n      javaObj.log('请求 ' + path + ' 异常：' + (e.message || e) + ' requestId=' + requestId)\n      javaObj.longToast('\\n请求异常\\n' + (e.message || e) + '\\nrequestId: ' + requestId)\n    } catch (e2) { }\n    return null\n  }\n}\nfunction deviceId() {\n  var javaObj = javaOf(this)\n  if (!javaObj) return ''\n  try {\n    return String(javaObj.androidId() || '').trim()\n  } catch (e1) {\n    return ''\n  }\n}\nfunction isAuthFailResponse(resp) {\n  if (!resp) return false\n  var code = 0\n  try { code = Number(resp.code()) } catch (e) { }\n  if (code === 401 || code === 403) return true\n  var body = ''\n  try { body = String(resp.body() || '') } catch (e2) { }\n  if (!body) return false\n  if (\/无效token|token\\s*过期|未登录|unauthorized\/i.test(body)) return true\n  try {\n    var j = JSON.parse(body)\n    if (!j) return false\n    var msg = String(j.msg || '')\n    var c = Number(j.code)\n    if ((c < 0 || c === 401 || c === 403) && \/token|登录|鉴权|授权|未登录\/i.test(msg)) return true\n  } catch (e3) { }\n  return false\n}\nfunction handleAuthFailInLoginCheck(resp) {\n  var javaObj = javaOf(this)\n  var bs = bookSourceOf(this)\n  if (!javaObj || !bs) return resp\n  if (String(bs.get('_authChecking') || '') === '1') return resp\n  var now = Date.now()\n  var last = Number(bs.get('_authReloginAt') || 0)\n  if (last && now - last < 45000) {\n    try { javaObj.toast('登录已过期，请重新登录同步') } catch (eT) { }\n    return resp\n  }\n  try { bs.put('_authReloginAt', String(now)) } catch (eP) { }\n  try { bs.setVariable('') } catch (e0) { }\n  try {\n    javaObj.longToast('\\n登录已过期或无效\\n即将打开登录页，完成后请关闭页面以同步 JWT')\n  } catch (e1) { }\n  try { bs.put('_authChecking', '1') } catch (eC) { }\n  try {\n    var id = deviceId.call(this)\n    var q = id ? ('?id=' + encodeURIComponent(id)) : ''\n    openConsole.call(this, '\/login' + q, true)\n    var token = getJwt.call(this)\n    if (!token) return resp\n    try {\n      javaObj.getHeaderMap().put('Authorization', 'Bearer ' + token)\n    } catch (eH) { }\n    try {\n      if (javaObj.getStrResponse) return javaObj.getStrResponse()\n    } catch (eR) {\n      try { javaObj.log('auth retry: ' + eR) } catch (eL) { }\n    }\n    return resp\n  } finally {\n    try { bs.put('_authChecking', '0') } catch (eF) { }\n  }\n}\nfunction openConsole(path, awaitCookie) {\n  refreshApi.call(this)\n  var javaObj = javaOf(this)\n  var bs = bookSourceOf(this)\n  if (!javaObj || !bs) return\n  var base = api + '\/console'\n  var p = String(path || '\/')\n  if (p.charAt(0) !== '\/') p = '\/' + p\n  var url = base + p\n  if (awaitCookie) {\n    javaObj.startBrowserAwait(url, '次元姬登录')\n    var token = ''\n    try {\n      token = String(cookie.getKey(api, COOKIE_TOKEN_KEY) || '').trim()\n    } catch (e0) {\n      try { token = String(javaObj.getCookie(api, COOKIE_TOKEN_KEY) || '').trim() } catch (e1) { }\n    }\n    if (!token) {\n      javaObj.longToast('\\n未检测到登录 Cookie（' + COOKIE_TOKEN_KEY + '）\\n请确认在阅读内置浏览器完成登录后关闭页面')\n      return\n    }\n    bs.setVariable(token)\n    javaObj.longToast('\\n✅登录同步成功，JWT 已写入源变量')\n  } else {\n    javaObj.startBrowser(url, '次元姬控制台')\n  }\n}\nfunction parseSearchKey(key) {\n  var keyword = String(key || '').trim()\n  var type = 'novel'\n  var mode = 'novel'\n  var share = false\n  var unavailable = false\n  function peel(re, t, m) {\n    if (re.test(keyword)) {\n      keyword = keyword.replace(re, '').trim()\n      if (t) type = t\n      if (m) mode = m\n      return true\n    }\n    return false\n  }\n  peel(\/^c(:|：)\/i, 'comic', 'comic')\n    || peel(\/^a(:|：)\/i, 'audio', 'audio')\n    || peel(\/^v(:|：)\/i, 'video', 'video')\n    || peel(\/^p(:|：)\/i, 'publication', 'publication')\n  if (\/^g(:|：)\/i.test(keyword)) {\n    keyword = keyword.replace(\/^g(:|：)\/i, '').trim()\n    share = true\n  }\n  if (type !== 'novel' && type !== 'comic') {\n    unavailable = true\n  }\n  return { keyword: keyword, type: type, mode: mode, share: share, unavailable: unavailable }\n}\nfunction resolveBookId(bId) {\n  if (bId && String(bId).match(\/^\\d+$\/)) return String(bId)\n  try {\n    if (typeof book !== 'undefined' && book && book.bookUrl) {\n      var m = String(book.bookUrl).match(\/[?&]bid=(\\d+)\/)\n      if (m) return m[1]\n    }\n  } catch (e0) { }\n  try {\n    var bs = bookSourceOf(this)\n    var t = bs && bs.get('tempBookId')\n    if (t) return String(t)\n  } catch (e1) { }\n  return String(bId || '')\n}\nfunction refreshExploreNow() {\n  var javaObj = javaOf(this)\n  var bs = bookSourceOf(this)\n  try {\n    if (javaObj && javaObj.refreshExplore) javaObj.refreshExplore()\n  } catch (e1) {\n    try { javaObj && javaObj.log('java.refreshExplore: ' + e1) } catch (eL) { }\n  }\n  try {\n    if (bs && bs.refreshExplore) bs.refreshExplore()\n  } catch (e2) {\n    try { javaObj && javaObj.log('source.refreshExplore: ' + e2) } catch (eL2) { }\n  }\n}\nfunction mapGet(key) {\n  var k = String(key || '')\n  function ok(v) {\n    if (v == null) return false\n    if (typeof v === 'object') return false\n    var s = String(v)\n    return s !== '' && s.indexOf('{') !== 0 && s !== '[object Object]'\n  }\n  try {\n    if (typeof infoMap !== 'undefined' && infoMap != null) {\n      var v = null\n      try {\n        var whole = infoMap.get()\n        if (whole != null) {\n          if (typeof whole.get === 'function') v = whole.get(k)\n          else v = whole[k]\n        }\n      } catch (e0) { v = null }\n      if (ok(v)) return String(v)\n    }\n  } catch (e) { }\n  try {\n    if (typeof result !== 'undefined' && result != null) {\n      var rv = null\n      try {\n        if (result.get && typeof result.get === 'function') rv = result.get(k)\n      } catch (eG) { rv = null }\n      if (ok(rv)) return String(rv)\n      try {\n        rv = result[k]\n        if (ok(rv)) return String(rv)\n      } catch (eB) { }\n    }\n  } catch (e2) { }\n  return ''\n}\nfunction pickLabel(arg, key, allowed) {\n  var v = (arg != null) ? String(arg) : ''\n  if (v && allowed.indexOf(v) >= 0) return v\n  v = mapGet(key)\n  if (v && allowed.indexOf(v) >= 0) return v\n  return ''\n}\nfunction setGender(g) {\n  var bs = bookSourceOf(this)\n  if (bs) bs.put('gender', String(g))\n  refreshExploreNow.call(this)\n  var javaObj = javaOf(this)\n  if (javaObj) javaObj.longToast('\\n✅发现页频道：' + (String(g) === '0' ? '女频' : '男频'))\n}\nfunction setOrder(order, desc) {\n  var bs = bookSourceOf(this)\n  if (bs) bs.put('order', String(order))\n  var javaObj = javaOf(this)\n  if (javaObj) javaObj.longToast('\\n✅分类排序：' + desc + '\\n打开分类书单时生效')\n}\nfunction setSearchRank(rank, desc) {\n  var bs = bookSourceOf(this)\n  if (bs) bs.put('searchRank', String(rank))\n  var javaObj = javaOf(this)\n  if (javaObj) javaObj.longToast('\\n✅搜索排序：' + desc)\n}\nfunction setExploreType(t) {\n  var bs = bookSourceOf(this)\n  if (bs) {\n    bs.put('exploreType', t)\n    bs.put('type', t)\n  }\n  var label = t\n  if (t == 'all') label = '全部'\n  else if (t == 'novel') label = '小说'\n  else if (t == 'comic') label = '漫画'\n  refreshExploreNow.call(this)\n  var javaObj = javaOf(this)\n  if (javaObj) javaObj.longToast('\\n✅发现类型：' + label)\n}\nfunction setExploreStyle(style) {\n  var bs = bookSourceOf(this)\n  if (bs) bs.put('exploreStyle', String(style || 'default'))\n  refreshExploreNow.call(this)\n  var javaObj = javaOf(this)\n  if (javaObj) javaObj.longToast('\\n✅发现样式：' + (style === 'compact' ? '紧凑（分类更密）' : '默认'))\n}\nfunction applyExploreTypeFromMap(arg) {\n  var v = pickLabel(arg, '发现类型', ['全部', '小说', '漫画'])\n  var t = 'all'\n  if (v === '小说') t = 'novel'\n  else if (v === '漫画') t = 'comic'\n  setExploreType.call(this, t)\n}\nfunction applyExploreGenderFromMap(arg) {\n  var v = pickLabel(arg, '小说频道', ['男频', '女频'])\n  setGender.call(this, v === '女频' ? '0' : '1')\n}\nfunction applyExploreStyleFromMap(arg) {\n  var v = pickLabel(arg, '发现样式', ['默认', '紧凑'])\n  setExploreStyle.call(this, v === '紧凑' ? 'compact' : 'default')\n}\nfunction applyOrderFromMap(arg) {\n  var allowed = ['人气最高', '订阅最多', '最近更新', '最近上架', '最近新书']\n  var v = pickLabel(arg, '分类排序', allowed)\n  var map = {\n    '人气最高': '1',\n    '订阅最多': '2',\n    '最近更新': '3',\n    '最近上架': '4',\n    '最近新书': '6',\n  }\n  setOrder.call(this, map[v] || '1', v || '人气最高')\n}\nfunction applySearchRankFromMap(arg) {\n  var v = pickLabel(arg, '搜索排序', ['按推荐', '按人气', '按销量', '按更新'])\n  var map = {\n    '按推荐': '0',\n    '按人气': '1',\n    '按销量': '2',\n    '按更新': '3',\n  }\n  setSearchRank.call(this, map[v] || '0', v || '按推荐')\n}\nfunction applyForceRefreshFromMap(arg) {\n  var v = pickLabel(arg, '强制刷新章节', ['开', '关'])\n  var on = (v === '开')\n  var bs = bookSourceOf(this)\n  if (bs) bs.put('强制刷新章节', on ? '1' : '0')\n  var javaObj = javaOf(this)\n  if (javaObj) {\n    javaObj.longToast(on\n      ? '\\n✅章节强制刷新已开启\\n无权限时服务端会忽略'\n      : '\\n✅章节强制刷新已关闭')\n  }\n}\nfunction syncExplorePrefsFromMap() {\n  var bs = bookSourceOf(this)\n  if (!bs) return\n  try {\n    if (typeof infoMap === 'undefined' || infoMap == null) return\n    var tl = mapGet('发现类型')\n    if (tl === '小说') { bs.put('exploreType', 'novel'); bs.put('type', 'novel') }\n    else if (tl === '漫画') { bs.put('exploreType', 'comic'); bs.put('type', 'comic') }\n    else if (tl === '全部') { bs.put('exploreType', 'all'); bs.put('type', 'all') }\n    var gl = mapGet('小说频道')\n    if (gl === '女频') bs.put('gender', '0')\n    else if (gl === '男频') bs.put('gender', '1')\n    var sl = mapGet('发现样式')\n    if (sl === '紧凑') bs.put('exploreStyle', 'compact')\n    else if (sl === '默认') bs.put('exploreStyle', 'default')\n  } catch (e) { }\n}\n","lastUpdateTime":"1792975970938","loginCheckJs":"; (function (a) {\n  try {\n    if (typeof isAuthFailResponse === 'function' && isAuthFailResponse(a)) {\n      return handleAuthFailInLoginCheck(a)\n    }\n  } catch (e) {\n    try { java.log('loginCheckJs: ' + e) } catch (e2) { }\n  }\n  return a\n})(result)\n","loginUi":"@js:\nvar fullStyle = { layout_flexGrow: 1, layout_flexBasisPercent: 1 }\nvar halfStyle = { layout_flexGrow: 1, layout_flexBasisPercent: 0.45 }\nvar thirdStyle = { layout_flexGrow: 1, layout_flexBasisPercent: 0.28 }\nfunction btn(name, action, style) {\n  return {\n    name: name,\n    type: 'button',\n    action: action || '',\n    style: style || halfStyle,\n  }\n}\nfunction hdr(title) {\n  return btn('✿ ' + title + ' ✿', \"java.toast('\" + title + \"')\", fullStyle)\n}\nfunction exploreTypeLabel() {\n  var t = ''\n  try { t = String(source.get('exploreType') || source.get('type') || 'all') } catch (e) { }\n  if (t === 'novel') return '小说'\n  if (t === 'comic') return '漫画'\n  return '全部'\n}\nfunction genderLabel() {\n  try {\n    return String(source.get('gender') || '1') === '0' ? '女频' : '男频'\n  } catch (e) {\n    return '男频'\n  }\n}\nfunction orderLabel() {\n  var o = ''\n  try { o = String(source.get('order') || '1') } catch (e) { }\n  var map = {\n    '1': '人气最高',\n    '2': '订阅最多',\n    '3': '最近更新',\n    '4': '最近上架',\n    '6': '最近新书',\n  }\n  return map[o] || '人气最高'\n}\nfunction searchRankLabel() {\n  var o = ''\n  try { o = String(source.get('searchRank') || '0') } catch (e) { }\n  var map = {\n    '0': '按推荐',\n    '1': '按人气',\n    '2': '按销量',\n    '3': '按更新',\n  }\n  return map[o] || '按推荐'\n}\nfunction styleLabel() {\n  try {\n    return String(source.get('exploreStyle') || 'default') === 'compact' ? '紧凑' : '默认'\n  } catch (e) {\n    return '默认'\n  }\n}\nfunction forceRefreshDefault() {\n  try {\n    return String(source.get('强制刷新章节') || '0') === '1' ? '开' : '关'\n  } catch (e) {\n    return '关'\n  }\n}\nfunction jwtEmoji() {\n  try {\n    return String(source.getVariable() || '').trim() ? '✅' : '❌'\n  } catch (e) {\n    return '❌'\n  }\n}\nvar apiDefault = ''\ntry {\n  apiDefault = String(source.get('apiHost') || '')\n} catch (e0) { }\nif (!apiDefault) {\n  try {\n    var info = JSON.parse(String(source.getLoginInfo() || '{}'))\n    apiDefault = String(info['API地址'] || '')\n  } catch (e1) { }\n}\nvar main = [\n  hdr('账号'),\n  btn('注册同步', 'syncRegister()', halfStyle),\n  btn('登录同步', 'syncLogin()', halfStyle),\n  btn('账号信息', 'openAccount()', thirdStyle),\n  btn('上游绑定', 'openUpstream()', thirdStyle),\n  btn('修改密码', 'openPassword()', thirdStyle),\n  btn('查询本地 JWT', 'queryJwt()', fullStyle),\n  hdr('发现设置'),\n  {\n    name: '发现类型',\n    type: 'select',\n    chars: ['全部', '小说', '漫画'],\n    default: exploreTypeLabel(),\n    action: \"applyExploreTypeFromMap(String((result.get&&result.get('发现类型'))||''))\",\n    style: halfStyle,\n  },\n  {\n    name: '小说频道',\n    type: 'select',\n    chars: ['男频', '女频'],\n    default: genderLabel(),\n    action: \"applyExploreGenderFromMap(String((result.get&&result.get('小说频道'))||''))\",\n    style: halfStyle,\n  },\n  {\n    name: '发现样式',\n    type: 'select',\n    chars: ['默认', '紧凑'],\n    default: styleLabel(),\n    action: \"applyExploreStyleFromMap(String((result.get&&result.get('发现样式'))||''))\",\n    style: halfStyle,\n  },\n  {\n    name: '分类排序',\n    type: 'select',\n    chars: ['人气最高', '订阅最多', '最近更新', '最近上架', '最近新书'],\n    default: orderLabel(),\n    action: \"applyOrderFromMap(String((result.get&&result.get('分类排序'))||''))\",\n    style: halfStyle,\n  },\n  {\n    name: '搜索排序',\n    type: 'select',\n    chars: ['按推荐', '按人气', '按销量', '按更新'],\n    default: searchRankLabel(),\n    action: \"applySearchRankFromMap(String((result.get&&result.get('搜索排序'))||''))\",\n    style: halfStyle,\n  },\n  hdr('调试'),\n  { name: 'API地址', type: 'text', default: apiDefault, style: fullStyle },\n  btn('保存 API 地址', 'saveApiHost()', fullStyle),\n  { name: '手动JWT', type: 'text', default: '', style: fullStyle },\n  btn('保存手动 JWT', 'saveManualJwt()', fullStyle),\n  hdr('下载 \/ 购买'),\n  {\n    name: '强制刷新章节',\n    type: 'toggle',\n    chars: ['关', '开'],\n    default: forceRefreshDefault(),\n    action: \"applyForceRefreshFromMap(String((result.get&&result.get('强制刷新章节'))||''))\",\n    style: fullStyle,\n  },\n  { name: '书籍ID', type: 'text', style: fullStyle },\n  btn('全本后台下载', 'downloadNovel()', fullStyle),\n  { name: '购买数量', type: 'text', default: '5', style: fullStyle },\n  btn('购买全本', 'buy(true)', halfStyle),\n  btn('购买指定数量', 'buy(false)', halfStyle),\n]\nresult = JSON.stringify(main)\n","loginUrl":"function login() { }\nfunction syncLogin() {\n  refreshApi.call(this)\n  var id = deviceId.call(this)\n  var q = id ? ('?id=' + encodeURIComponent(id)) : ''\n  openConsole.call(this, '\/login' + q, true)\n}\nfunction syncRegister() {\n  refreshApi.call(this)\n  var id = deviceId.call(this)\n  if (!id) {\n    java.longToast('\\n无法获取设备 id（oaid\/androidId），仍打开注册页')\n  }\n  var q = id ? ('?id=' + encodeURIComponent(id)) : ''\n  openConsole.call(this, '\/register' + q, true)\n}\nfunction openAccount() {\n  openConsole.call(this, '\/', false)\n}\nfunction openUpstream() {\n  openConsole.call(this, '\/upstream', false)\n}\nfunction openPassword() {\n  openConsole.call(this, '\/password', false)\n}\nfunction saveApiHost() {\n  var host = String((result && (result['API地址'] || result.get && result.get('API地址'))) || '').trim().replace(\/\\\/$\/, '')\n  if (host && !\/^https?:\\\/\\\/\/i.test(host)) {\n    return java.longToast('\\n❌API地址须以 http:\/\/ 或 https:\/\/ 开头')\n  }\n  source.put('apiHost', host)\n  try {\n    var info = JSON.parse(String(source.getLoginInfo() || '{}'))\n    info['API地址'] = host\n    source.putLoginInfo(JSON.stringify(info))\n  } catch (e) { }\n  try {\n    var map = new Packages.java.util.HashMap()\n    map.put('API地址', host)\n    java.upLoginData(map)\n  } catch (e2) { }\n  java.longToast(host ? ('\\n✅API地址已保存：' + host) : '\\n✅已恢复默认 API 地址')\n}\nfunction saveManualJwt() {\n  var token = String((result && (result['手动JWT'] || result.get && result.get('手动JWT'))) || '').trim()\n  if (!token) {\n    return java.longToast('\\n❌请粘贴 JWT')\n  }\n  source.setVariable(token)\n  java.longToast('\\n✅JWT 已写入源变量')\n}\nfunction switchForceChapRefresh() {\n  var old = source.get('强制刷新章节') || '0'\n  var on = old != '1'\n  source.put('强制刷新章节', on ? '1' : '0')\n  java.longToast(on\n    ? '\\n✅章节强制刷新已开启\\n无权限时服务端会忽略'\n    : '\\n✅章节强制刷新已关闭')\n}\nfunction downloadNovel() {\n  var bid = String((result && (result['书籍ID'] || result.get && result.get('书籍ID'))) || '').trim()\n  if (!\/^\\d+$\/.test(bid)) {\n    return java.toast('请输入有效书籍ID')\n  }\n  var data = requestApi.call(this, '\/novel\/download', { bid: bid })\n  if (!data) return\n  java.longToast('\\n✅全本下载已提交')\n}\nfunction buy(all) {\n  var bid = String((result && (result['书籍ID'] || result.get && result.get('书籍ID'))) || '').trim()\n  if (!\/^\\d+$\/.test(bid)) {\n    return java.longToast('\\n❌请输入有效书籍ID')\n  }\n  var data\n  if (all) {\n    data = requestApi.call(this, '\/novel\/buy_novel', { bid: bid })\n  } else {\n    var num = Number(String((result && (result['购买数量'] || result.get && result.get('购买数量'))) || '5'))\n    if (!num || num <= 0) num = 5\n    data = requestApi.call(this, '\/novel\/buy_chapter', { bid: bid, num: num })\n  }\n  if (!data) return\n  java.longToast('\\n✅购买完成')\n}\nfunction queryJwt() {\n  var token = getJwt.call(this)\n  if (!token) {\n    return java.longToast('\\n❌尚未登录，请先「注册同步」或「登录同步」')\n  }\n  java.longToast('\\n✅源变量已有 JWT（长度 ' + token.length + '）\\n账号\/上游请到 Web 控制台查看')\n}\n","respondTime":180000,"ruleBookInfo":{"author":"@js:\nvar r=asBook(result); r.author || ''","coverUrl":"@js:\nvar r=asBook(result); r.cover || ''","init":"@js:\nvar d = result\ntry { if (typeof d === 'string') d = JSON.parse(d) } catch (e) { d = {} }\ntry {\n  if (d && d.data != null && typeof d.data === 'object' && !Array.isArray(d.data)\n    && (d.code != null || d.msg != null) && d.bid == null && d.type == null) {\n    d = d.data\n  }\n} catch (eWrap) { }\nvar bid = ''\ntry { bid = String((d && d.bid) || (d && d.id) || '') } catch (e0) { }\nif (!bid) {\n  try {\n    var m = String(baseUrl || '').match(\/[?&]bid=(\\d+)\/)\n    if (m) bid = m[1]\n  } catch (e1) { }\n}\nvar t = ''\ntry { t = String((d && d.type) || '') } catch (eType) { t = '' }\nif (!t) {\n  try {\n    var mBs = String(baseUrl || '').match(\/[?&]_bsType=([^&,]+)\/)\n    if (mBs && mBs[1]) t = decodeURIComponent(mBs[1])\n  } catch (eBs) { }\n}\nif (!t) {\n  try { t = String(source.get('searchMode') || 'novel') } catch (eSm) { t = 'novel' }\n}\nif (!t) t = 'novel'\nif (d && typeof d === 'object') {\n  if (!d.bid && bid) d.bid = bid\n  if (!d.id && bid) d.id = bid\n  d.type = t === 'tts' ? 'novel' : t\n  d.mode = t\n}\ntry { if (bid) source.put('tempBookId', String(bid)) } catch (e3) { }\nJSON.stringify(d || {})\n","intro":"@js:\nvar rData = {}\ntry {\n  rData = (typeof asBook === 'function') ? asBook(result) : (\n    (typeof result === 'string') ? JSON.parse(result) : (result || {})\n  )\n} catch (e) { rData = {} }\nfunction esc(s) {\n  s = (s == null) ? '' : String(s)\n  return s.replace(\/&\/g, '&amp;').replace(\/<\/g, '&lt;').replace(\/>\/g, '&gt;').replace(\/\"\/g, '&quot;')\n}\nfunction firstAuthorChar(name) {\n  var s = String(name || '')\n  var stripped = s.replace(\/^[^\\u4e00-\\u9fffA-Za-z]+\/, '')\n  if (!stripped) stripped = s.trim()\n  return stripped ? stripped.charAt(0) : '作'\n}\nfunction pickAccent() {\n  var hues = [12, 28, 152, 168, 188, 205, 222, 248, 332]\n  var h = hues[Math.floor(Math.random() * hues.length)]\n  var s = 44 + Math.floor(Math.random() * 14)\n  var l = 34 + Math.floor(Math.random() * 8)\n  var c1 = 'hsl(' + h + ' ' + s + '% ' + (l - 7) + '%)'\n  var c2 = 'hsl(' + h + ' ' + (s + 6) + '% ' + l + '%)'\n  var c3 = 'hsl(' + h + ' ' + s + '% ' + (l - 4) + '%)'\n  return {\n    heroBg: 'linear-gradient(145deg, ' + c1 + ' 0%, ' + c2 + ' 45%, ' + c3 + ' 100%)',\n    accent: 'hsl(' + h + ' ' + (s + 4) + '% ' + l + '%)',\n    accentSoft: 'hsla(' + h + ' ' + (s + 4) + '% ' + l + '% \/ 0.16)',\n    shadow: 'hsla(' + h + ' ' + s + '% ' + l + '% \/ 0.3)',\n  }\n}\nvar authorName = esc(rData.author || '')\nvar authorInitial = esc(firstAuthorChar(rData.author || ''))\nvar bookName = esc(rData.name || '本书')\nvar introDesc = String(rData.desc || '').trim()\nvar introDescHtml = esc(introDesc)\nvar accent = pickAccent()\nvar status = esc(rData.status || '')\nvar category = [rData.category, rData.subCategory].filter(Boolean).map(esc).join(' · ')\nvar html = `\n<useweb>\n<style>\n:root {\n  --ink: #1c1917;\n  --mute: #78716c;\n  --line: #e7e5e4;\n  --paper: #fafaf9;\n  --panel: #ffffff;\n  --accent: ${accent.accent};\n  --accent-soft: ${accent.accentSoft};\n  --radius: 14px;\n  --font: \"Segoe UI\", \"PingFang SC\", \"Noto Sans SC\", system-ui, sans-serif;\n}\n* { box-sizing: border-box; }\nbody { margin: 0; font-family: var(--font); color: var(--ink); background: transparent; }\n.wrap {\n  width: min(520px, 94vw);\n  margin: 10px auto 18px;\n  display: flex;\n  flex-direction: column;\n  gap: 12px;\n}\n.hero {\n  background: ${accent.heroBg};\n  color: #f8fafc;\n  border-radius: var(--radius);\n  padding: 16px;\n  display: flex;\n  gap: 12px;\n  align-items: center;\n  box-shadow: 0 10px 28px ${accent.shadow};\n}\n.av.ph {\n  width: 52px; height: 52px; border-radius: 50%;\n  flex-shrink: 0;\n  border: 2px solid rgba(255,255,255,.35);\n  background: rgba(255,255,255,.15);\n  display: flex; align-items: center; justify-content: center;\n  font-size: 1.2rem; font-weight: 750; line-height: 1;\n  color: #f8fafc;\n}\n.h-main { min-width: 0; flex: 1; }\n.h-name { font-size: 1.05rem; font-weight: 750; }\n.h-book { margin-top: 6px; font-size: 0.72rem; opacity: .7; }\n.h-meta { margin-top: 4px; font-size: 0.72rem; opacity: .75; }\n.panel {\n  background: var(--panel);\n  border: 1px solid var(--line);\n  border-radius: var(--radius);\n  padding: 14px 14px 12px;\n}\n.panel-h { margin-bottom: 10px; }\n.panel-h h2 { margin: 0; font-size: 0.95rem; font-weight: 750; }\n.dossier {\n  margin: 0;\n  white-space: pre-wrap;\n  word-break: break-word;\n  font-size: 0.82rem;\n  line-height: 1.65;\n  color: #292524;\n  background: var(--paper);\n  border-radius: 10px;\n  padding: 12px;\n  border-left: 3px solid var(--accent);\n}\n<\/style>\n<div class=\"wrap\">\n  <section class=\"hero\">\n    <span class=\"av ph\">${authorInitial}<\/span>\n    <div class=\"h-main\">\n      <div class=\"h-name\"><span>${authorName || '作者'}<\/span><\/div>\n      <div class=\"h-book\">${bookName}<\/div>\n      ${status || category ? `<div class=\"h-meta\">${[status, category].filter(Boolean).join(' · ')}<\/div>` : ''}\n    <\/div>\n  <\/section>\n  <section class=\"panel\">\n    <div class=\"panel-h\"><h2>本书档案<\/h2><\/div>\n    <pre class=\"dossier\">${introDescHtml || '暂无简介'}<\/pre>\n  <\/section>\n<\/div>\n<\/useweb>\n`\nhtml\n","kind":"@js:\nvar r=asBook(result);\n[r.status, r.category, r.subCategory, r.tag].filter(Boolean).join(',')\n","lastChapter":"@js:\nvar r=asBook(result); r.lastChapter || ''","name":"@js:\nvar r=asBook(result); r.name || ''","tocUrl":"@js:\nfunction pickStr() {\n  for (var i = 0; i < arguments.length; i++) {\n    var v = arguments[i]\n    if (v == null) continue\n    try { v = String(v).trim() } catch (e) { continue }\n    if (v && v !== 'null' && v !== 'undefined') return v\n  }\n  return ''\n}\nvar bid = pickStr(\n  java.getString('bid'),\n  java.getString('id'),\n  (typeof result === 'object' && result && result.bid),\n  (typeof result === 'object' && result && result.id),\n  source.get('tempBookId')\n)\nif (!bid) {\n  try {\n    var mBid = String(baseUrl || '').match(\/[?&]bid=(\\d+)\/)\n    if (mBid) bid = mBid[1]\n  } catch (e0) { }\n}\nvar t = pickStr(\n  java.getString('mode'),\n  java.getString('type'),\n  (typeof result === 'object' && result && result.mode),\n  (typeof result === 'object' && result && result.type),\n  'novel'\n)\nif (!bid) {\n  try { java.toast('无法解析书籍ID，无法加载目录') } catch (e3) { }\n}\ntry { source.put('tempBookId', String(bid)) } catch (e4) { }\n'data:;base64,' + java.base64Encode(String(bid)) + ',{\"type\":\"' + (t || 'novel') + '\",\"bid\":\"' + bid + '\"}'\n","wordCount":"@js:\nvar r=asBook(result); r.charCount || ''"},"ruleContent":{"callBackJs":"if (event == 'clickCustomButton' || event == 'longClickCustomButton') {\n  try { source.put('menuTag', 'main') } catch (e) { }\n  java.open('login')\n  true\n} else {\n  false\n}\n","content":"@js:\nvar cid = java.hexDecodeToString(result)\nvar meta = JSON.parse(baseUrl.slice(baseUrl.indexOf('{')))\nvar type = meta.type || 'novel'\nvar bid = meta.bid\nif (type == 'novel' || type == 'tts') {\n  var chapParams = { bid: bid, cid: cid }\n  try {\n    if (String(source.get('强制刷新章节') || '') == '1') chapParams.forceRefresh = '1'\n  } catch (eFr) { }\n  var data = requestApi.call(this, '\/novel\/chap', chapParams)\n  if (!data || data.content == null) {\n    ''\n  } else {\n    String(data.content)\n  }\n} else if (type == 'comic') {\n  var comicParams = { bid: bid, cid: cid }\n  try {\n    if (String(source.get('强制刷新章节') || '') == '1') comicParams.forceRefresh = '1'\n  } catch (eFr2) { }\n  var comicData = requestApi.call(this, '\/comic\/chap', comicParams)\n  var pics = comicData && comicData.pics\n  if (!pics || !pics.length) {\n    ''\n  } else {\n    var html = ''\n    for (var i = 0; i < pics.length; i++) {\n      var pic = pics[i]\n      var imgSrc = ''\n      if (typeof pic === 'string') {\n        imgSrc = pic\n      } else if (pic) {\n        imgSrc = String(pic.imgUrl || pic.url || '')\n      }\n      if (imgSrc) html += '<img src=\"' + imgSrc + '\">'\n    }\n    html\n  }\n} else {\n  java.longToast('\\n暂不支持正文类型：' + type)\n  ''\n}\n","imageStyle":"FULL","payAction":";(function () {\n  try {\n    var type = ''\n    var mType = String(chapter.url).match(\/\"type\"\\s*:\\s*\"([^\"]+)\"\/)\n    if (mType) type = mType[1]\n    if (type != 'novel' && type != 'comic') {\n      java.toast('\\n当前类型不支持章节购买：' + (type || 'unknown'))\n      return false\n    }\n    var bid = ''\n    var mBid = String(chapter.url).match(\/\"bid\"\\s*:\\s*\"(\\d+)\"\/)\n    if (mBid) bid = mBid[1]\n    if (!bid) {\n      var mBook = String(book.bookUrl || '').match(\/bid=(\\d+)\/)\n      if (mBook) bid = mBook[1]\n    }\n    var chapId = ''\n    var mB64 = String(chapter.url).match(\/base64,([^,]+)\/)\n    if (mB64) chapId = java.base64Decode(mB64[1])\n    if (!bid || !chapId) {\n      java.toast('\\n无法解析书籍\/章节 ID')\n      return false\n    }\n    var path = type === 'comic' ? '\/comic\/buy_chapter' : '\/novel\/buy_chapter'\n    var data = requestApi.call(this, path, {\n      bid: bid,\n      cid: chapId,\n    })\n    if (data) {\n      java.longToast('\\n✅购买成功，请刷新章节')\n      return true\n    }\n    return false\n  } catch (e) {\n    java.log('payAction: ' + e)\n    return false\n  }\n}).call(this)\n"},"ruleExplore":{"author":"author","bookList":"$.data.books[*]","bookUrl":"@js:\nvar t = java.getString('type') || 'novel'\nvar bid = java.getString('bid') || java.getString('id')\nvar searchMode = ''\ntry { searchMode = String(source.get('searchMode') || '').trim() } catch (e) { }\nif (!t || t === 'null') t = 'novel'\nvar infoType = t\nvar params = { bid: bid }\nif (searchMode && searchMode !== 'novel') params._bsType = searchMode\nelse if (t && t !== 'novel') params._bsType = t\ngetApiUrl('\/' + infoType + '\/info', params)\n","coverUrl":"cover","intro":"desc","kind":"{{$.status}}\n{{$.category}}\n{{$.subCategory}}\n","lastChapter":"lastChapter","name":"name","wordCount":"charCount"},"ruleSearch":{"author":"author","bookList":"$.data[*]","bookUrl":"@js:\nvar t = java.getString('type') || 'novel'\nvar bid = java.getString('bid') || java.getString('id')\nvar searchMode = ''\ntry { searchMode = String(source.get('searchMode') || '').trim() } catch (e) { }\nif (!t || t === 'null') t = 'novel'\nvar infoType = t\nvar params = { bid: bid }\nif (searchMode && searchMode !== 'novel') params._bsType = searchMode\nelse if (t && t !== 'novel') params._bsType = t\ngetApiUrl('\/' + infoType + '\/info', params)\n","checkKeyWord":"诡秘之主","coverUrl":"cover","intro":"desc","kind":"{{$.status}}\n{{$.category}}\n{{$.subCategory}}\n","lastChapter":"lastChapter","name":"name","wordCount":"charCount"},"ruleToc":{"chapterList":"@js:\nvar bid = ''\ntry { bid = String(java.hexDecodeToString(result) || '').trim() } catch (e0) { bid = '' }\nif (!\/^\\d+$\/.test(bid)) {\n  try {\n    var raw = String(result == null ? '' : result).trim()\n    if (\/^\\d+$\/.test(raw)) bid = raw\n  } catch (e1) { }\n}\nvar meta = {}\ntry {\n  meta = JSON.parse(baseUrl.slice(baseUrl.indexOf('{')))\n} catch (e2) { meta = {} }\nvar type = String(meta.type || '').trim()\nif (!type || type === 'null' || type === 'undefined') type = 'novel'\ntry {\n  var metaBid = String(meta.bid || '').trim()\n  if (\/^\\d+$\/.test(metaBid)) bid = metaBid\n} catch (e3) { }\nif (!bid) {\n  try { bid = String(source.get('tempBookId') || '').trim() } catch (e4) { }\n}\nvar apiType = (type == 'tts') ? 'novel' : type\nif (!apiType) apiType = 'novel'\nvar chaps = []\nif (!bid) {\n  java.toast('无法解析书籍ID，获取目录失败')\n} else if (apiType !== 'novel' && apiType !== 'comic') {\n  java.longToast('\\n暂未开放目录类型：' + apiType)\n} else {\n  book.type = apiType === 'comic' ? 64 : 8\n  try {\n    var loginInfo = JSON.parse(source.getLoginInfo() || '{}')\n    loginInfo.书籍ID = bid\n    source.putLoginInfo(JSON.stringify(loginInfo))\n  } catch (eLi) { }\n  var catalog = requestApi.call(this, '\/' + apiType + '\/catalog', { bid: bid }) || []\n  var sharedRows = requestApi.call(this, '\/' + apiType + '\/cache', { bid: bid }) || []\n  var shareMap = {}\n  for (var ci = 0; ci < sharedRows.length; ci++) {\n    var d = sharedRows[ci]\n    var id = d.cid != null ? d.cid : d.chapId\n    if (id != null) shareMap[String(id)] = d\n  }\n  function fmtTime(timestamp) {\n    var date = new Date(timestamp)\n    var year = date.getFullYear()\n    var month = String(date.getMonth() + 1).padStart(2, '0')\n    var day = String(date.getDate()).padStart(2, '0')\n    var hours = String(date.getHours()).padStart(2, '0')\n    var minutes = String(date.getMinutes()).padStart(2, '0')\n    return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes\n  }\n  for (var vi = 0; vi < catalog.length; vi++) {\n    var volume = catalog[vi]\n    chaps.push({\n      name: volume.volumeName,\n      voltype: true,\n    })\n    var chapters = volume.chapters || []\n    for (var chi = 0; chi < chapters.length; chi++) {\n      var chapRow = chapters[chi]\n      var chapId = chapRow.cid\n      var chapName = chapRow.chapName\n      var count = apiType === 'comic' ? (chapRow.picCount || 0) : (chapRow.charCount || 0)\n      var unit = apiType === 'comic' ? '页' : '字'\n      var isVip = chapRow.isVip\n      var isBuy = chapRow.isBuy\n      var displayTime = chapRow.displayTime\n      var isShare = !!shareMap[String(chapId)]\n      var url = 'data:;base64,' + java.base64Encode(String(chapId)) + ',{\"type\":\"' + type + '\",\"bid\":\"' + bid + '\"}'\n      chaps.push({\n        id: chapId,\n        name: (isVip ? (isBuy ? ' 🔑 ' : isShare ? '  🍎 ' : '') : '') + chapName,\n        url: url,\n        info: fmtTime(displayTime) + ' | ' + (count + unit) + (isShare ? '  🍎 ' : isBuy ? ' 🔑 ' : ''),\n        isVip: !isShare && isVip,\n        isBuy: isBuy,\n      })\n    }\n  }\n}\nchaps\n","chapterName":"name","chapterUrl":"url","isPay":"isBuy","isVip":"isVip","isVolume":"voltype","updateTime":"info"},"searchUrl":"@js:\nvar parsed = parseSearchKey.call(this, key)\ntry { source.put('searchMode', parsed.mode) } catch (e) { }\nif (parsed.unavailable) {\n  java.longToast('\\n暂未开放：' + parsed.type + '\\n当前支持小说 \/ 漫画（c:）（可用 g: 共享）')\n  getApiUrl('\/novel\/search', { keyword: '0', page: 1, share: true })\n} else {\n  var apiType = parsed.mode === 'comic' ? 'comic' : 'novel'\n  var params = {\n    keyword: parsed.keyword || key,\n    page: page,\n  }\n  if (parsed.share) params.share = true\n  try {\n    var rank = String(source.get('searchRank') || '').trim()\n    if (rank === '0' || rank === '1' || rank === '2' || rank === '3') {\n      params.rankType = rank\n    }\n  } catch (e2) { }\n  getApiUrl('\/' + apiType + '\/search', params)\n}\n","variableComment":"JWT（由登录同步自动写入；也可手动粘贴）","weight":0}]