[
  {
    "bookSourceComment": "建议登录\n\n酷安 @吉王义昊\nGitHub：https://github.com/jiwangyihao/source-j-legado\n\n# 关于许可的额外声明（在线版本参见 GitHub，以在线版本为准）\n\n- 当许可证与本声明冲突时，以本声明为准；\n- 对于本仓库中的任意代码片段：按照 `MPL 2.0` 中有关约定执行；\n- 对于本仓库中的某一完整书源的转载或二次开发，需满足以下全部条件：\n  - 在[本仓库](https://github.com/jiwangyihao/source-j-legado)的 `issue` 中提出请求并具体说明转载地址、二次开发后的书源开源地址以及其他必要信息；\n  - 等待原作者（即本仓库的初始所有者和初始代码贡献者 [@jiwangyihao](https://github.com/jiwangyihao)）查看并通过 `issue` 或依据原作者要求更改转载方式或补充更详细的信息。\n  - 考虑到本项目弃坑的可能，新 `issue` 开启后超过 20 个工作日原作者没有回复或者原作者回复要求更改的 `issue` 在更改后超过 20 个工作日原作者没有回复即视为原作者通过该 `issue`：\n    - 此处的 `issue` 仅包括在[本仓库](https://github.com/jiwangyihao/source-j-legado)开启的，处于「开启状态」的 issue。（也就是说，请不要在已经关闭的 issue 中回复）。\n    - 对于此种方式通过的 issue，转载/二次开发者仍应当遵守本声明中已经写明的相关约定。\n  - 不得上传至源仓库或整理至`非轻小说专用`的书源合集中并应当避免其他人将转载/二次开发版本上传至源仓库或整理至`非轻小说专用`的书源合集中：\n    - 关于轻小说的定义的额外说明：不包括国内的原创网络文学作品（如 `SF 轻小说` 中的原创轻小说以及`起点中文网`中标签包含轻小说的作品）。\n    - 轻小说专用的定义：有且仅有想看轻小说的人可能会添加。\n  - 必须在转载/二次开发地址的明显位置完整包含本声明的全部内容。\n  - 必须保留源注释中原有的更改记录。\n\n整理修改缝合：酷安 Wolken\n灰色章节修复目录部分By叶落岚起+关耳/乃星改\n2021.8.3\n补丁 : 神秘人\n修复搜索问题\n修复发现榜单没图的问题\n修复章节内图片问题\n新補丁: 神秘人\n抓取源码中的关键字替换\n更新补丁\n更新补丁17/9\n2023.9.27\n新补丁：酷安 @吉王义昊\n重新抓取源码中的关键字替换\n2023.9.27\n新补丁2nd：酷安 @吉王义昊\n修复插图不能正常加载的问题\n2023.9.30\n酷安 @吉王义昊\n添加登录URL\n清理了无用代码\n使用更易读的变量名\n2023.10.1\n酷安 @吉王义昊\n重新抓取源码中的关键字替换\n2023.10.1\n酷安 @吉王义昊\n在获取正文时自动拉取源码并解密进行关键字替换，一劳永逸（大概）解决问题\n注意：此版本会使网络请求次数增加一倍，并一定程度上减慢加载速度\n2023.10.1\n酷安 @吉王义昊\n增加登录URL和登录UI\n启用CookieJar\n去除章节名下方的URL\n修正了下一页获取导致的正文加载错误\n补全发现中的文库\n修正部分小说目录获取错误的问题\n2023.10.4\n酷安 @吉王义昊\n跟进源码新的加密方式\n2023.10.5\n酷安 @吉王义昊\n跟进源码新的加密方式\n2023.10.6\n酷安 @吉王义昊\n跟进源站网站结构改动\n2023.10.7\n酷安 @吉王义昊\n改进了目录获取方式，现在能够处理更多意外情况\n2023.10.7\n酷安 @吉王义昊 @是樱椛不是樱花\n发现新增标签\n2023.10.9\n酷安 @吉王义昊\n改进了发现\n2023.10.13\n酷安 @吉王义昊\n跟进源站结构改动\n2023.10.15\n酷安 @吉王义昊\n修正部分小说目录获取错误的问题\n2023.10.22\n酷安 @吉王义昊\n发现页更改为动态获取，为大家带来更多排列组合（乐\n同样由于动态获取，现在可以查看标签对应的书籍数量了\n小幅优化正文的获取速度\n正文页可显示正确的章节名称（在加载对应正文页后目录里的错误名称会自动更正）\n2023.10.23\n酷安 @吉王义昊\n加载详情页后会自动更改错误的书名\n2023.10.25\n酷安 @吉王义昊\n发现页新增书架\n2023.11.7\n酷安 @吉王义昊\n使用Cache小幅度加快连续加载时正文的加载速度\n2023.11.11\n酷安 @吉王义昊\n解决了部分书籍目录最后一章获取链接错误的问题\n2023.11.12\n酷安 @吉王义昊\n移除了站长的喊话\n2023.11.14\n酷安 @吉王义昊\n修复了图片加载错误的问题\n2023.11.16\n酷安 @吉王义昊\n解决了部分书籍获取目录失败的问题\n2023.11.23\n酷安 @吉王义昊\n解决了未登录时无法正确加载发现页的问题\n2023.11.28\n酷安 @吉王义昊\nwrap了一份WebCrypto API，现在搜索不用等待内置浏览器跳转了\n2023.12.12\n酷安 @吉王义昊\n适配新版卷名\n2023.12.13\n酷安 @吉王义昊\n适配新版卷名\n2024.1.13\n酷安 @吉王义昊\n自动调用内置浏览器过cf盾\n移除了并发率\n将UA置空强制阅读使用WebView默认UA\n目录下一页识别改为简繁通用\n2024.1.20\n酷安 @吉王义昊\n实验性加入自动重试机制，大幅提高目录及多页正文加载成功率\n2024.1.24\n酷安 @吉王义昊\n适配最新正文检测\n2024.2.6\n酷安 @吉王义昊\n处理cf扩大化问题\n解决部分小说目录加载失败的问题（《谁说从妥协开始的恋爱一定没结果》）\n2024.2.14\n酷安 @吉王义昊\n正文重试优化\n2024.3.11\n酷安 @吉王义昊\n适配最新正文检测\n处理登录失败问题\n2024.3.25\n酷安 @吉王义昊\n适配最新文库筛选\n2024.4.8\n酷安 @吉王义昊\n回退更改以解决乱码问题\n适配最新标签筛选\n调整发现样式\n2024.4.12\n酷安 @吉王义昊\n在简介中增加标签信息\n2024.5.2\n酷安 @吉王义昊\n解决换字体导致的乱码问题\n2024.6.11\n酷安 @吉王义昊\n处理源站最新更改\n2024.7.6\n酷安 @吉王义昊\n跟进源站发现更改\n2024.9.17\n酷安 @吉王义昊\n适配源站正文更改\n2025.1.10\n酷安 @吉王义昊\n处理「客户端已停用」问题\n跟进最新正文字体加密\n过滤采集Note\n2025.2.23\n酷安 @吉王义昊\n修复登录问题\n修复正文加载\n移除意外出现的注释\n2025.4.20\n酷安 @吉王义昊\n修复目录解析问题（感谢 @夜澤川）\n修复正文加载\n2025.5.2\n酷安 @吉王义昊\n字体加载换至 gcore.jsdelivr.net\n处理浏览器检测\n修复正文加载\n2025.7.25\n酷安 @吉王义昊\n解决正文乱序问题\n2025.8.7\n修复标题中有单引号时正文无法加载的问题（感谢 https://github.com/VirtualTowel #27）\n2025.8.12\n@VirtualTowel\n增加空行显示\n2025.8.24\n@VirtualTowel\n修复正文中包含尖括号导致正文乱序的问题\n2025.10.25\n修复登录，修正标签（感谢https://github.com/Dark-Tide #41）\n2025.12.11\n固定哔轻中的重要cookie（感谢https://github.com/Dark-Tide #48）\n2026.1.15\n酷安 @吉王义昊\n修复正文加载",
    "bookSourceGroup": "轻小说",
    "bookSourceName": "哔哩轻小说🏷",
    "bookSourceType": 0,
    "bookSourceUrl": "https://www.bilinovel.com",
    "concurrentRate": "1000",
    "customOrder": 1,
    "enabled": true,
    "enabledCookieJar": true,
    "enabledExplore": true,
    "exploreUrl": "@js:\nres = []\n\n//筛选链接生成\nfunction generateFilterUrl(new_values) {\n  values = {\n    order: 'lastupdate',\n    anime: '0',\n    tagid: '0',\n    sortid: '0',\n    typeid: '0',\n    words: '0',\n    rgroupid: '0',\n    update: '0',\n    isfull: '0',\n    page: '{{page}}',\n    initial: '0'\n  }\n  for (key in new_values) {\n  \t  values[key]=new_values[key]\n  \t}\n  return source.bookSourceUrl + '/wenku/' + values.order + '_' + values.tagid + '_' + values.isfull + '_' + values.anime + '_' + values.rgroupid + '_' + values.sortid + '_' + values.typeid + '_' + values.words + '_' + values.page + '_' + values.update + '.html';\n}\n\n//书架\nuser = ajax(source.bookSourceUrl + \"/user.php\")\n//java.toast(user)\ntry {\nif (user.match(/<title>(登录|错误).+哔哩轻小说<\\/title>/gi)) {\n  //未登录\n  res.push(\n    {\n      title: `>> 我的书架 | 未登录 <<`,\n      url: '',\n      style: { layout_flexGrow: 1, layout_flexBasisPercent: 1 }\n    })\n} else {\n  //已登录\n  res.push(\n    {\n      title: `>> 我的书架 | ${user.match(/<span class=\"user-name\">.+<\\/span>/gi)[0].replace(/<\\/?span.*?>/gi,'')} <<`,\n      url: '',\n      style: { layout_flexGrow: 1, layout_flexBasisPercent: 1 }\n    })\n  bookcase = ajax(source.bookSourceUrl + \"/bookcase.php\")\n  bookcase.match(/<select[\\s\\S]+?<\\/select>/gi)[0].match(/<option.+<\\/option>/gi).forEach(option => {\n    res.push(\n      {\n        title: option.match(/>.+(?=<)/gi)[0].replace('>', ''),\n        url: source.bookSourceUrl + \"/bookcase.php?classid=\" + option.match(/value=\\\".+?(?=\\\")/gi)[0].replace(/(value=\\\")/gi, ''),\n        style: { layout_flexGrow: 1 }\n      })\n  })\n}\n} catch (e) {\n  throw(\"书架加载失败：\\n\"+e+\"\\n登录页源码：\\n\"+user)\n}\n\ncontent = org.jsoup.Jsoup.parse(ajax(source.bookSourceUrl + \"/wenku/\"))\ntry {\ncontent.select(\"ul#filters li.sort-li\").forEach(li => {\n  res.push({\n    title: `>> ${li.select(\"h3\").textNodes()[0].text()} <<`,\n    url: \"\",\n    style: {\n      layout_flexGrow: 1,\n      layout_flexBasisPercent: 1\n    }\n  })\n  li.select(\"a\").forEach(a => {\n    let filter = {}\n    let type = a.attr(\"data-filter-type\")\n    let value = a.attr(\"data-filter-value\")\n    filter[type]=value\n    res.push({\n      title: a.text(),\n      url: generateFilterUrl(filter),\n      style: { layout_flexGrow: 1 }\n    })\n  })\n})\n} catch (e) {\n  throw(\"文库加载失败：\\n\"+e+\"\\n文库页源码：\\n\"+content)\n}\n\nJSON.stringify(res)\n//throw(JSON.stringify(res)) ",
    "header": "{\n\"Referer\": \"https://www.bilinovel.com/\",\n\"User-Agent\":\"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Mobile Safari/537.36 EdgA/135.0.0.0\",\n\"Accept-Language\":\"zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6\",\n\"Cache-Control\": \"no-cache\",\n\"Cookie\": \"night=0\",\n\"Accept\":\"*/*\"\n}",
    "jsLib": "function errorReg() {\n  return /(java.net.+Exception)|(okhttp.+Exception)|(error code: 1015)|(cf-error[\\s\\S]*1015)|(503 Service Temporarily Unavailable)|(403 Forbidden)|(Parse error.+in.+wwwroot.+php)|(_cf_chl_opt|challenges.css)|(<center.+>.+不支持)|(章节内容不支持该浏览器显示)|(内容加载失败！)|(客戶端停用中)/gi\n}\nfunction ajax(url) {\n  const { java, cookie } = this;\n  let content = \"\";\n  let time=1;\n  \tcontent = java.ajax(url);\n  \twhile (String(content).match(errorReg())) {\n    if (time>10) {\n  \t    java.toast(\"重试10次后仍请求失败：\"+url)\n  \t    throw(\"重试10次后仍请求失败：\"+url+content)\n  \t    break;\n    \t}\n    \tif (!!String(content).match(/403|1015/gi)) {\n    \t  const nowTime = new Date().getTime()\n    \t  const waitTime = (Math.random() + 1) * time * 10\n    \t  java.toast(`访问频率过高，等待 ${waitTime} 秒`)\n    \t  while (new Date().getTime() < nowTime + waitTime * 1000) {}\n    \t}\n    \tif (!!String(content).match(/不支持/gi)) {\n      //java.log(cookie.getCookie(url));\n      cookie.replaceCookie(url,java.webView(null,url,\"document.cookie\"));\n    \t}\n    \tif (!!String(content).match(/_cf_chl_opt|challenges.css|403|客戶端停用中/gi)) {\n    \t\t let i = 1;\n      \twhile (!!String(content).match(/_cf_chl_opt|challenges.css|403|客戶端停用中/gi)) {\n\t       //java.log('盾');\n\t       //java.log(content);\n        if (i <= 1) {\n          content = java.webView(null,url,null);\n        } else {\n          java.toast(\"啊哦，静默破盾好像不管用，试试手动过校验吧！\");\n          content = java.startBrowserAwait(url,\"加载完毕后点完成，此页面可能会弹出多次\").body();\n        }\n        i++;\n      }\n      continue;\n    }\n  \t\t java.log(\"请求失败：\"+url)\n  \t\t java.log(\"重试\"+time+\"次\")\n  \t\t let t=new Date().getTime()\n    while (new Date().getTime() - t < 500) { }\n    if (String(content).match(/1015|503|php/gi)) {\n    \t  while (new Date().getTime() - t < 5000) { }\n    \t}\n  \t\t content = java.ajax(url);\n  \t\t time++;\n  \t}\n  \treturn String(content);\n}\n\n/**\n * 通用日志代理创建函数\n * @param {object} target - 被代理的目标对象\n * @param {string} name - 在日志中显示的代理名称\n * @returns {Proxy} - 返回一个带日志记录功能的代理对象\n */\nfunction createLoggingProxy(target, name) {\n  const { java } = this;\n  return new Proxy(target, {\n    get(target, prop, receiver) {\n      //java.log(`${name}`, prop);\n      return Reflect.get(target, prop, receiver);\n    },\n    set(target, prop, value) {\n      //java.log(`set ${name}`, prop, value);\n      return Reflect.set(target, prop, value);\n    },\n  });\n};\n\n/**\n * 模拟 ClassList 对象的工厂函数\n * @returns {Proxy}\n */\nfunction createClassList() {\n    const classList = {\n        add(...args) {\n            // java.log(\"classList add\", args);\n        },\n    };\n    return createLoggingProxy(classList, \"classList\");\n};\n\n\n/**\n * 通用模拟元素构造工厂函数\n * @param {string} tagName - 元素的标签名 (e.g., 'P', 'STYLE')\n * @param {object} [properties={}] - 附加到元素上的额外属性和方法\n * @param {string} [logName] - 日志记录中使用的名称，默认为 tagName 的小写\n * @returns {Proxy} - 返回一个模拟的 DOM 元素代理\n */\nfunction createMockElement(tagName, properties, logName) {\n  // 手动处理默认参数，以兼容不支持默认参数语法的旧引擎\n  properties = properties || {};\n\n  const upperTagName = tagName.toUpperCase();\n  const baseElement = {\n    nodeType: 1,\n    tagName: upperTagName\n  };\n\n  Object.assign(baseElement, properties);\n\n  return createLoggingProxy(baseElement, logName || tagName.toLowerCase());\n};\n\n/**\n * 专门用于创建 P 标签代理的函数，整合了所有 P 标签的共有行为\n * @param {string} pOuterHTML - P 标签的 outerHTML 字符串\n * @returns {Proxy}\n */\nfunction createParagraphProxy(pOuterHTML) {\n    const innerHTML = pOuterHTML.replace(/<\\/?p>/g, \"\");\n    return createMockElement('P', {\n        innerHTML: innerHTML,\n        classList: createClassList(),\n        get outerHTML() {\n            return `<p>${this.innerHTML}</p >`;\n        },\n    }, 'paragraph');\n};",
    "lastUpdateTime": 1773586709871,
    "loginUi": "[\n  {\n    \"name\": \"账号\",\n    \"type\": \"text\"\n  },\n  {\n    \"name\": \"密码\",\n    \"type\": \"password\"\n  }\n]",
    "loginUrl": "@js:\nfunction login() {\n  let une = source.getLoginInfoMap().get(\"账号\")\n  let pwd = source.getLoginInfoMap().get(\"密码\")\n  if (une && pwd) {\n    let body = String('username=' + une + '&password=' + pwd + '&usecookie=86400&act=login')\n    let url = source.bookSourceUrl + '/login.php?do=submit'\n    ajax(url)\n    let res = java.post(url, body, {\n      \"Content-Type\": \"application/x-www-form-urlencoded\",\n      \"Accept-Language\": \"zh-CN\",\n      \"Cookie\": \"night=0\",\n      \"User-Agent\": \"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Mobile Safari/537.36 EdgA/135.0.0.0\"\n    })\n    let ck = res.cookies()\n    if (res.body().match(/错误/gi)) {\n      throw(res.body().match(/<div class=\"aui-ver-form\">[\\s\\S]+<br>/gi)[0].replace(/<.+>\\s*/gi,\"\"))\n    }\n    let header = JSON.stringify({\n      \"Cookie\": String(ck).match(/\\{(.*?)\\}/)[1].replace(/,/g, ';') + (ck.night ? '' : '; night=0') // 登录头优先级更高，请求头规则的cookie键会被登录头覆盖\n    })\n    source.putLoginHeader(header)\n  }\n}",
    "respondTime": 180000,
    "ruleBookInfo": {
      "author": "[property=\"og:novel:author\"]@content",
      "canReName": "1",
      "coverUrl": ".book-cover@src",
      "intro": "标签：{{@@class.tag-small red@text##\\s## - }}\n{{@@id.bookSummary@tag.content@textNodes}}",
      "kind": "{{@@class.book-cell@tag.p.1@text##\\|##,}}\n{{@@class.tag-small red@text}}\n##.*万字\\s*|·.*",
      "lastChapter": "class.gray ell@text##(\\d+-\\d+-\\d+\\s\\d+:\\d+)·(.*)##$2 • $1",
      "name": "[property=\"og:novel:book_name\"]@content",
      "tocUrl": "{{@@class.btn-normal red@href}}",
      "wordCount": "class.book-cell@tag.p.1@text##\\s*\\|.*"
    },
    "ruleContent": {
      "content": "@js:\nif (String(src).match(errorReg())) {\n  java.log(src);\n  src = ajax(baseUrl);\n}\nsrc = src\n  .replace(/<\\w\\d{4}>([^<]+(<br\\s*\\/?>)?)+<\\/\\w\\d{4}>/gi, \"\")\n  .replace(/<!--[\\s\\S]*?-->/gi, \"\")\n  // 将p标签内的所有尖括号替换为大于小于号\n  .replace(/(<p>)(.*?)(<\\/p>)/g, (match, p1, content, p3) => {\n    return p1 + content.replace(/</g, '＜').replace(/>/g, '＞') + p3;\n  });\njava.setContent(src);\n\n/*\nif (java.get('jsContent') == '' || java.get('id') == '') {\n  jsLink = java.getString(\"tag.script@src\").match(/http.+zhmb.+readtool.+\\?v.+/gi)\n  try {\n    jsLink.forEach(link => {\n      jsRaw = java.cacheFile(link)\n      jsContentMatch = jsRaw.match(/.+\\\\x.+/gi)\n      if (jsContentMatch) {\n        java.put('jsContent', jsContentMatch[0])\n      }\n\n      idMatch = jsRaw.match(/contentid: '([^']+)'/)\n      if (idMatch && idMatch[1]) {\n        java.put('id', idMatch[1])\n      }\n    })\n  \t} catch(e) {\n  \t\t throw(\"正文获取js错误：\\n\"+e+\"\\n正文源码：\\n\"+src)\n  \t}\n}\njava.put('id', \"acontent\")\ntry {\n  h = String(java.getString(\"id.\" + java.get('id') + \"@html\"))\n  h = h.replace(\"&nbsp;&nbsp;\", \"&emsp;\")\n  el = {\n    innerHTML: String(h),\n    appendChild: (el) => {},\n    insertBefore: (el) => {}\n  }\n  document = {\n    getElementById: () => { return el },\n    createElement: (tagName) => {\n  \t    return el\n    },\n    head: el,\n    body: el\n  }\n  setInterval = function(cb) {\n  \t  cb()\n  \t}\n  window = { document: document, RegExp: RegExp, setInterval: setInterval }\n  rEval = eval\n  eval = (str) => { java.log(str); rEval(str) }\n  //rEval(String(java.get('jsContent')))\n} catch(e) {\n  java.log(src)\n  throw(\"js 执行错误：\"+e+\"\\njs 内容：\\n\"+java.get('jsContent'))\n}\nh = el.innerHTML\n*/\n\nif (java.get(\"jsContent\") == \"\" || java.get(\"id\") == \"\") {\n  jsLink = java\n    .getString(\"tag.script@src\")\n    .match(/http.+zhmb.+chapterlog.+\\?v.+/gi);\n  try {\n    jsLink.forEach((link) => {\n      jsRaw = java.cacheFile(link);\n      jsContentMatch = jsRaw.match(/.+\\\\x.+/gi);\n      if (jsContentMatch) {\n        java.put(\"jsContent\", jsContentMatch[0]);\n      }\n    });\n  } catch (e) {\n    throw \"正文获取js错误：\\n\" + e + \"\\n正文源码：\\n\" + src;\n  }\n}\njava.put(\"id\", \"acontent\");\ntry {\n  paramJs = java\n    .getString(\"tag.script@html\")\n    .match(/<script.+ReadParams.+<\\/script>/gi)[0]\n    .replace(/<.?script[^>]*>/gi, \"\")\n    .replace(\n      /(\\w+\\s*:\\s*')(.*?)(?='\\s*[,}])/g,\n      (_, prefix, content) => prefix + content.replace(/'/g, \"\\\\'\")\n    );\n  eval(paramJs);\n  h = String(java.getString(\"id.\" + java.get(\"id\") + \"@html\"));\n  h = h.replace(\"&nbsp;&nbsp;\", \"&emsp;\");\n  const acontentTarget = {\n    innerHTML: String(h),\n    get childNodes() {\n      const tagRegex = /<p>.*?<\\/p>|<img[^>]*>/g;\n      const matchedTags = this.innerHTML.match(tagRegex) || [];\n\n      return matchedTags\n        .map((outerHTML) => {\n          if (outerHTML.startsWith(\"<p>\")) {\n            return createParagraphProxy(outerHTML);\n          }\n\n          if (outerHTML.startsWith(\"<img\")) {\n            return createMockElement(\n              \"IMG\",\n              {\n                innerHTML: null,\n                get src() {\n                  const match = outerHTML.match(/src=\"([^\"]*)\"/);\n                  return match ? match[1] : \"\";\n                },\n                get outerHTML() {\n                  return outerHTML;\n                },\n              },\n              \"image\"\n            );\n          }\n\n          return null;\n        })\n        .filter(Boolean); // 过滤掉任何无法识别的标签所产生的 null\n    },\n    get children() {\n      // children 和 childNodes 在这个模拟中行为一致\n      return this.childNodes;\n    },\n    appendChild(element) {\n      this.innerHTML = (this.innerHTML ?? \"\") + element.outerHTML;\n    },\n    querySelectorAll(selector) {\n      // 简化实现，假设总是查询 'p'\n      if (selector.toLowerCase() === \"p\") {\n        return (this.innerHTML.match(/<p>(.*?)<\\/p>/g) || []).map(\n          createParagraphProxy\n        );\n      }\n      return [];\n    },\n    insertBefore(...args) {\n      // java.log(\"insertBefore\", args);\n    },\n  };\n\n  const elements = {\n    \"#acontent\": createLoggingProxy(acontentTarget, \"acontent\"),\n  };\n\n  // 使用工厂函数重构 document 对象\n  const documentTarget = {\n    querySelector(selector) {\n      return elements[selector] || null;\n    },\n    createElement(tagName) {\n      let properties = {};\n      // 为 STYLE 标签添加特殊的 sheet 属性\n      if (tagName.toUpperCase() === \"STYLE\") {\n        properties.sheet = {\n          insertRule(rule) {\n            // java.log(\"insertRule\", rule);\n          },\n        };\n      }\n      return createMockElement(tagName, properties);\n    },\n    head: createMockElement(\n      \"HEAD\",\n      {\n        appendChild(element) {\n          // java.log(\"head appendChild\", element);\n        },\n      },\n      \"head\"\n    ),\n  };\n\n  // 获取空行索引\n  var tagRegex;\n  tagRegex = /<p>.*?<\\/p>|<img[^>]*>|<br>/g;\n  const matchedTags = acontentTarget.innerHTML.match(tagRegex) || [];\n  const blankLines = matchedTags\n    .map((element, index) => (element === \"<br>\" ? index : -1)) // 匹配返回索引，否则返回 -1\n    .filter((index) => index !== -1); // 过滤掉 -1\n\n  // 全局 document 对象\n  var document = createLoggingProxy(documentTarget, \"document\");\n  setInterval = function (cb) {\n    cb();\n  };\n  setTimeout = setInterval;\n  addEventListener = () => {};\n  removeEventListener = () => {};\n  window = { document: document, RegExp: RegExp, setInterval: setInterval, setTimeout: setTimeout, addEventListener: addEventListener, removeEventListener: removeEventListener };\n  rEval = eval;\n  eval = (str) => {\n    java.log(str);\n    rEval(str);\n  };\n  rEval(String(java.get(\"jsContent\")));\n} catch (e) {\n  java.log(src);\n  throw \"js 执行错误：\" + e + \"\\njs 内容：\\n\" + java.get(\"jsContent\");\n}\nh = acontentTarget.innerHTML;\n\n// 增加空行\ntagRegex = /<p>.*?<\\/p >|<img[^>]*>/g;\nh = h.match(tagRegex) || [];\nblankLines.forEach(index => {\n    if (index <= h.length) { // 防止越界\n        h.splice(index, 0, \"<p>⁡</p>\"); // 在 index 处插入一个隐形字符\n    }\n});\nh = h.join(\"\");\n\nexpandedStyleSrc = src;\nstyleJs = src.match(/<script>.+font.+<\\/script>/gi);\nif (styleJs) {\n  styleJs = styleJs[0].replace(/<.?script>/gi, \"\");\n  try {\n    rEval = eval;\n    eval = function (script) {\n      const expandLines = script.match(\n        /[\\n;][^\\n;]+=\\[[^\\n;]*\\.\\.\\.[^\\n;]*\\][\\n;]/gi\n      );\n      if (expandLines) {\n        expandLines.forEach((line) => {\n          l = line.replace(/[\\n;]/gi, \"\");\n          varName = l.split(\"=\")[0];\n          arrExpr = l\n            .split(\"=\")[1]\n            .replace(/^\\[|\\]$/gi, \"\")\n            .split(/,\\s*/g);\n          result = varName + \"=[];\";\n          arrExpr.forEach((expr) => {\n            if (/^\\.\\.\\./i.test(expr)) {\n              result +=\n                varName +\n                \"=\" +\n                varName +\n                \".concat(\" +\n                expr.replace(/^\\.\\.\\./i, \"\") +\n                \");\";\n            } else {\n              result += varName + \".push(\" + expr + \")\";\n            }\n          });\n          script = script.replace(line, \"\\n;\" + result + \";\\n\");\n        });\n      }\n      try {\n        rEval(script);\n      } catch (e) {\n        throw \"\\n嵌套 Eval 执行错误：\" + e + \"\\n嵌套 js 内容：\" + script + \"\\n\";\n      }\n    };\n\n    function CSSStyleSheet() {\n      this.raw = \"\";\n      this.replaceSync = function (rule) {\n        this.raw = rule;\n      };\n    }\n    rEval(styleJs);\n    eval = rEval;\n    document.adoptedStyleSheets.forEach((css) => {\n      expandedStyleSrc += \"<style>\" + css.raw + \"</style>\";\n    });\n  } catch (e) {\n    java.log(src);\n    throw \"style js 执行错误：\" + e + \"\\njs 内容；\\n\" + styleJs;\n  }\n}\n\nexpandedStyleSrc.match(/<style>.+<\\/style>/gi).forEach((style) => {\n  style = style.replace(/<.?style>/gi, \"\");\n  if (style.match(/[^{}]*@font-face[^{}]*{[^{}]+}/gi)) {\n    fontRules = {};\n    style\n      .match(/[^{}]*@font-face[^{}]*{[^{}]+}/gi)[0]\n      .replace(/[^{}]*@font-face[^{}]*{([^{}]+)}/gi, \"$1\")\n      .split(\";\")\n      .forEach((rule) => {\n        if (rule.split(\":\")[0].trim())\n          fontRules[rule.split(\":\")[0].trim()] = rule.split(\":\")[1].trim();\n      });\n    java.log(JSON.stringify(fontRules));\n    //TODO: 懒得写了，解析字体地址\n\n    style\n      .replace(/[^{}]*@font-face[^{}]*{([^{}]+)}/gi, \"\")\n      .match(/[^{}]+{[^{}]+}/gi)\n      .forEach((set) => {\n        selector = set.replace(/([^{}]+){([^{}]+)}/gi, \"$1\").trim();\n        rules = {};\n        set\n          .replace(/([^{}]+){([^{}]+)}/gi, \"$2\")\n          .split(\";\")\n          .forEach((rule) => {\n            if (rule.split(\":\")[0].trim())\n              rules[rule.split(\":\")[0].trim()] = rule.split(\":\")[1].trim();\n          });\n        if (rules[\"font-family\"]) {\n          if (rules[\"font-family\"].match(fontRules[\"font-family\"])) {\n            doc = org.jsoup.Jsoup.parse(h);\n            java.log(h);\n            java.log(selector);\n            originText = doc.select(selector).text();\n            java.log(originText);\n            replaceText = java.replaceFont(\n              originText,\n              java.queryTTF(\n                \"https://gcore.jsdelivr.net/gh/jiwangyihao/source-j-legado@main/utils/read.ttf\"\n              ),\n              java.queryTTF(\n                \"https://gcore.jsdelivr.net/gh/jiwangyihao/source-j-legado@main/utils/MI LANTING.ttf\"\n              ),\n              true\n            );\n            java.log(replaceText);\n            h = h.replace(/<\\/?\\w+>/g, \"\").replace(originText, replaceText);\n          }\n        }\n      });\n  }\n});\nh;\n",
      "imageStyle": "DEFAULT",
      "nextContentUrl": "##url_next:'([^']*)'##$1###\n@js:\n//121_2.html这样的是下一页，纯数字则是下一章\n//带有catalog是详情页\njava.log(\"url: \"+result)\nif (result==\"\") java.log(src)\nvar isNew=/(\\/(\\d+).html)|catalog/.test(result);\nvar out=isNew?'':result;\nout",
      "payAction": "@js:\nbaseUrl",
      "replaceRegex": "##((?<=[\\u4e00-\\u9fa5“‘「（，])\\s+)?<!--\\s*\\（继续下一页\\）\\s*-->\\s*|((?<=[\\u4e00-\\u9fa5“‘「（，])\\s+)?\\（本章未完\\）\\s*|.+tmygod.+\\n",
      "title": "id.atitle@text"
    },
    "ruleExplore": {
      "author": "class.book-author@ownText",
      "bookList": "@js:\ni = 1;\n\nif (String(src).match(errorReg())) {\n  src=ajax(baseUrl);\n}\njava.setContent(src)\nresult = java.getElement(\"class.book-ol@tag.li\");\nresult;",
      "bookUrl": "a@href",
      "coverUrl": "img@data-src",
      "intro": "class.book-desc@text&&class.ell@text",
      "kind": "class.tag-small-group origin-right@tag.em.0@text&&\nclass.tag-small-group origin-right@tag.em.1@text&&\ntag.time@text\n@js:\nres=[]\nresult.forEach(item=>{\n    item.split(new RegExp(\"[ \\/]\",\"gi\")).forEach(i=>res.push(i))\n})\nres",
      "name": "class.book-li@tag.img@alt",
      "wordCount": "class.tag-small blue@text"
    },
    "ruleSearch": {
      "author": "class.book-author@textNodes",
      "bookList": "@js:\njava.log(cookie.getCookie(baseUrl))\nif (String(result).match(errorReg())&&!result.includes(\"somework\")) {\n  result=ajax(baseUrl);\n}\nif (result.includes(\"no-js\")) {\n  java.log(result)\n  jsContent = java.cacheFile(java.getString(\"tag.script@src\").match(/http.+somework.+\\?v.+/gi)[0])\n  window = {\n    a: result.match(/window.a=\\'.+\\'/gi)[0].replace(/window.a=|\\'/gi, ''),\n    b: result.match(/window.b=\\'.+\\'/gi)[0].replace(/window.b=|\\'/gi, ''),\n    c: result.match(/window.c=\\'.+\\'/gi)[0].replace(/window.c=|\\'/gi, ''),\n    crypto: {\n      subtle: {\n        importKey(format, keyData, algorithm, extractable, keyUsages) {\n          return {\n            then(func) { func(keyData) }\n          }\n        },\n        decrypt(algorithm, key, data) {\n          cipher = java.createSymmetricCrypto(\"AES/CTR/NoPadding\", key, algorithm.counter)\n          return {\n            then: (func) => { func(cipher.decryptStr(data)) }\n          }\n        }\n      }\n    }\n  }\n  setTimeout = () => { }\n  function TextDecoder() { }\n  TextDecoder.prototype.decode = bytes => bytes\n  document = { cookie: \"\" }\n  java.log(jsContent)\n  funcName=String(jsContent).match(/^function.+\\(/gi)[0].replace(/function| |\\(/gi,'')\n  \tjava.log(funcName)\n  eval(funcName + ' = str => java.base64DecodeToByteArray(str)')\n  eval(String(jsContent)\n    .replace(/^function.+\\(/gi, 'function ('))\n  java.log(document.cookie)\n  cookie.replaceCookie(baseUrl, document.cookie)\n  do {\n    t = new Date().getTime()\n    while (new Date().getTime() - t < 5000) { }\n    java.setContent(result = ajax(baseUrl + \",\"\n      + JSON.stringify({\n        headers: {\n          Cookie: cookie.getCookie(baseUrl)\n        }\n      })))\n    java.log(result)\n  } while (result.includes(\"no-js\"))\n}\njava.getElement(\"class.book-ol book-ol-normal@tag.li\")",
      "bookUrl": "tag.a.0@href",
      "checkKeyWord": "我的青春恋爱喜剧",
      "coverUrl": "a@href@js:\nvar id = result.match(/\\/(\\d+)\\.html/)[1];\n'https://www.linovelib.com/files/article/image/'+parseInt(id/1000)+'/'+id+'/'+id+'s.jpg';",
      "intro": "class.book-desc@text",
      "kind": "class.tag-small-group origin-right@tag.em@text\n@js:\nres=[]\nresult.forEach(item=>{\n    item.split(new RegExp(\"[ \\/]\",\"gi\")).forEach(i=>res.push(i))\n})\nres",
      "name": "class.book-title-x@class.book-title@text"
    },
    "ruleToc": {
      "chapterList": "@js:\nif (String(src).match(errorReg())) {\n  src=ajax(baseUrl);\n}\njava.setContent(src)\nresult = java.getElement(\"id.catelogX@.chapter-li:not(.volume-cover)\");\n    //现实debug(尝试修复正文链接问题，和目录不全)\n    //《好友角色的我怎么可能大受欢迎》第三卷12\n    //《我的青春恋爱喜剧》\n    //2022-8-19\n    //原来的代码在源注释（已移除）\n    //2022-8-20修复https://w.linovelib.com/novel/2765.html目录加载失败\n    //2023-9-30使用易于理解的变量命名\n    //2023-10-7处理cid(1)以及连续多个cid(0)\n    //2023-11-11解决了一个原来手滑写出的bug\n    //2023-11-16处理连续多个卷名\n    //2023-12-12适配新版卷名\n    //2024-2-6处理《谁说从妥协开始的恋爱一定没结果》\n\nres = result\n//java.log(res)\n\nfor (i = 0; i < res.length; i++) {\n    java.setContent(res[i])\n    if (java.getString(\"tag.a@href\").match(/javascript:cid\\(.+\\)/gi)) {\n        if (String(res[(i == res.length - 1?i:i+1)]).match(/javascript:cid\\(.+\\)/gi)) {\n            java.setContent(res[i - 1])\n            prevLink = java.getString(\"a:not(:has(h3))@href\")\n            if (prevLink == \"\") {\n                java.setContent(res[i - 2])\n                prevLink = java.getString(\"tag.a@href\")\n            }\n            content = ajax(source.bookSourceUrl + prevLink)\n            java.setContent(content)\n            foot = java.getElements(\"id.footlink\")\n            path = prevLink\n\n            //java.log(foot)\n            while (String(foot).match(/下一页|下一頁/gi)) {\n            \t    //java.log(foot)\n                str = content.match(/<script type\\=\\\"text\\/javascript\\\">var ReadParams.*/)\n                path = String(str).match(/url_next\\:'.*?html/)[0].replace(\"url_next:'\", \"\")\n                content = java.ajax(\"https://w.linovelib.com\" + path)\n                java.setContent(content)\n                foot = java.getElements(\"id.footlink\")\n            }\n\n            next = ajax(source.bookSourceUrl + path)\n            str = next.match(/<script type\\=\\\"text\\/javascript\\\">var ReadParams.*/)\n            path = String(str).match(/url_next\\:'.*?html/)[0].replace(\"url_next:'\", \"\")\n            java.log('l:'+path)\n            res[i] = String(res[i]).replace(/javascript:cid\\(.+\\)/gi, path)\n        } else {\n            nextLink = \"\"\n            for (var j=1;nextLink==\"\";j++) {\n                java.setContent(res[i + j])\n                nextLink = java.getString(\"a:not(:has(h3))@href\")\n            }\n            next = ajax(source.bookSourceUrl + nextLink)\n            str = next.match(/<script type\\=\\\"text\\/javascript\\\">var ReadParams.*/)\n            try {\n              path = String(str).match(/url_previous\\:'.*?html/)[0].replace(\"url_previous:'\", \"\")\n            } catch(e) {\n            \t  java.toast(String(next)+\"\\n\")\n            \t  //java.log(next)\n            \t  throw(e+\"目录解析报错开始：\\n\"+String(next)+\"\\n结束\")\n            \t}\n            //java.log(path)\n\n            res[i] = String(res[i]).replace(/javascript:cid\\(.+\\)/gi, path)\n        }\n    }\n\n    //java.log(res[i])\n}\nres",
      "chapterName": "text",
      "chapterUrl": "@js:\n//只有不为卷名时返回URL，避免阅读自动合并\njava.getString(\"class.chapter-bar@text\")!=java.getString(\"text\") ? java.getString(\"tag.a@href\") : \"\"",
      "isVolume": "@js:\njava.getString(\"class.chapter-bar@text\")==java.getString(\"text\")"
    },
    "searchUrl": "<js>\npage == 1 ?\n  '/search.html,' + JSON.stringify({\n  \"body\": \"searchkey={{key}}\",\n  \"method\": \"POST\"\n})\n  :\n  '/search/{{key}}_{{page}}.html'\n</js>",
    "weight": 0
  },
  {
    "bookSourceComment": "建议登录\n\n酷安 @吉王义昊\nGitHub：https://github.com/jiwangyihao/source-j-legado\n\n# 关于许可的额外声明（在线版本参见 GitHub，以在线版本为准）\n\n- 当许可证与本声明冲突时，以本声明为准；\n- 对于本仓库中的任意代码片段：按照 `MPL 2.0` 中有关约定执行；\n- 对于本仓库中的某一完整书源的转载或二次开发，需满足以下全部条件：\n  - 在[本仓库](https://github.com/jiwangyihao/source-j-legado)的 `issue` 中提出请求并具体说明转载地址、二次开发后的书源开源地址以及其他必要信息；\n  - 等待原作者（即本仓库的初始所有者和初始代码贡献者 [@jiwangyihao](https://github.com/jiwangyihao)）查看并通过 `issue` 或依据原作者要求更改转载方式或补充更详细的信息。\n  - 考虑到本项目弃坑的可能，新 `issue` 开启后超过 20 个工作日原作者没有回复或者原作者回复要求更改的 `issue` 在更改后超过 20 个工作日原作者没有回复即视为原作者通过该 `issue`：\n    - 此处的 `issue` 仅包括在[本仓库](https://github.com/jiwangyihao/source-j-legado)开启的，处于「开启状态」的 issue。（也就是说，请不要在已经关闭的 issue 中回复）。\n    - 对于此种方式通过的 issue，转载/二次开发者仍应当遵守本声明中已经写明的相关约定。\n  - 不得上传至源仓库或整理至`非轻小说专用`的书源合集中并应当避免其他人将转载/二次开发版本上传至源仓库或整理至`非轻小说专用`的书源合集中：\n    - 关于轻小说的定义的额外说明：不包括国内的原创网络文学作品（如 `SF 轻小说` 中的原创轻小说以及`起点中文网`中标签包含轻小说的作品）。\n    - 轻小说专用的定义：有且仅有想看轻小说的人可能会添加。\n  - 必须在转载/二次开发地址的明显位置完整包含本声明的全部内容。\n  - 必须保留源注释中原有的更改记录。\n\n整理修改缝合：酷安 Wolken\n灰色章节修复目录部分By叶落岚起+关耳/乃星改2021.8.3\n补丁 : 神秘人\n修复搜索问题\n修复发现榜单没图的问题\n修复章节内图片问题\n新補丁: 神秘人\n抓取源码中的关键字替换\n更新补丁\n更新补丁17/9\n2023.9.27\n新补丁：酷安 @吉王义昊\n重新抓取源码中的关键字替换\n2023.9.27\n新补丁2nd：酷安 @吉王义昊\n修复插图不能正常加载的问题\n2023.9.30\n酷安 @吉王义昊\n添加登录URL\n清理了无用代码\n使用更易读的变量名\n2023.10.1\n酷安 @吉王义昊\n重新抓取源码中的关键字替换\n2023.10.1\n酷安 @吉王义昊\n在获取正文时自动拉取源码并解密进行关键字替换，一劳永逸（大概）解决问题\n注意：此版本会使网络请求次数增加一倍，并一定程度上减慢加载速度\n2023.10.1\n酷安 @吉王义昊\n增加登录URL和登录UI\n启用CookieJar\n去除章节名下方的URL\n修正了下一页获取导致的正文加载错误\n补全发现中的文库\n修正部分小说目录获取错误的问题\n2023.10.4\n酷安 @吉王义昊\n跟进源码新的加密方式\n2023.10.5\n酷安 @吉王义昊\n跟进源码新的加密方式\n2023.10.6\n酷安 @吉王义昊\n跟进源站网站结构改动\n2023.10.7\n酷安 @吉王义昊\n改进了目录获取方式，现在能够处理更多意外情况\n2023.10.7\n酷安 @吉王义昊 @是樱椛不是樱花\n发现新增标签\n2023.10.9\n酷安 @吉王义昊\n改进了发现\n2023.10.13\n酷安 @吉王义昊\n跟进源站结构改动\n2023.10.15\n酷安 @吉王义昊\n修正部分小说目录获取错误的问题\n2023.10.22\n酷安 @吉王义昊\n发现页更改为动态获取，为大家带来更多排列组合（乐\n同样由于动态获取，现在可以查看标签对应的书籍数量了\n小幅优化正文的获取速度\n2023.10.22\n酷安 @吉王义昊\n为大家带来——台版哔哩轻小说！\n正文页可显示正确的章节名称（在加载对应正文页后目录里的错误名称会自动更正）\n2023.10.23\n酷安 @吉王义昊\n加载详情页后会自动更改错误的书名\n2023.10.25\n酷安 @吉王义昊\n发现页新增书架\n2023.11.11\n酷安 @吉王义昊\n解决了部分书籍目录最后一章获取链接错误的问题\n2023.11.14\n酷安 @吉王义昊\n修复了图片加载错误的问题\n2023.11.16\n酷安 @吉王义昊\n解决了部分书籍获取目录失败的问题\n2023.12.12\n酷安 @吉王义昊\n适配新版卷名\n2023.12.13\n酷安 @吉王义昊\n适配新版卷名\n2024.1.13\n酷安 @吉王义昊\n目录下一页识别改为简繁通用\n2024.3.11\n酷安 @吉王义昊\n适配最新正文检测\n2024.3.25\n酷安 @吉王义昊\n适配最新文库筛选\n2024.4.8\n酷安@吉王义昊\n回退更改以解决乱码问题\n适配最新标签筛选\n调整发现样式\n2024.4.12\n酷安 @吉王义昊\n在简介中增加标签信息\n2024.7.6\n酷安 @吉王义昊\n跟进源站发现更改",
    "bookSourceGroup": "轻小说",
    "bookSourceName": "哔哩轻小说•台",
    "bookSourceType": 0,
    "bookSourceUrl": "https://tw.linovelib.com",
    "customOrder": 21,
    "enabled": true,
    "enabledCookieJar": true,
    "enabledExplore": true,
    "exploreUrl": "@js:\nres = []\n\n//筛选链接生成\nfunction generateFilterUrl(new_values) {\n  values = {\n    order: 'lastupdate',\n    anime: '0',\n    tagid: '0',\n    sortid: '0',\n    typeid: '0',\n    words: '0',\n    rgroupid: '0',\n    update: '0',\n    isfull: '0',\n    page: '{{page}}',\n    initial: '0'\n  }\n  for (key in new_values) {\n  \t  values[key]=new_values[key]\n  \t}\n  return source.bookSourceUrl + '/wenku/' + values.order + '_' + values.tagid + '_' + values.isfull + '_' + values.anime + '_' + values.rgroupid + '_' + values.sortid + '_' + values.typeid + '_' + values.words + '_' + values.page + '_' + values.update + '.html';\n}\n\n//书架\nuser = java.t2s(java.ajax(\"https://tw.linovelib.com/user.php\"))\ntry {\nif (user.match(/<title>(登入|错误).+哔哩轻小说<\\/title>/gi)) {\n  //未登录\n  res.push(\n    {\n      title: `>> 我的书架 | 未登录 <<`,\n      url: '',\n      style: { layout_flexGrow: 1, layout_flexBasisPercent: 1 }\n    })\n} else {\n  //已登录\n  res.push(\n    {\n      title: `>> 我的书架 | ${user.match(/<span class=\"user-name\">.+<\\/span>/gi)[0].replace(/<\\/?span.*?>/gi,'')} <<`,\n      url: '',\n      style: { layout_flexGrow: 1, layout_flexBasisPercent: 1 }\n    })\n  bookcase = java.t2s(java.ajax(\"https://tw.linovelib.com/bookcase.php\"))\n  bookcase.match(/<select[\\s\\S]+?<\\/select>/gi)[0].match(/<option.+<\\/option>/gi).forEach(option => {\n    res.push(\n      {\n        title: option.match(/>.+(?=<)/gi)[0].replace('>', ''),\n        url: \"https://tw.linovelib.com/bookcase.php?classid=\" + option.match(/value=\\\".+?(?=\\\")/gi)[0].replace(/(value=\\\")/gi, ''),\n        style: { layout_flexGrow: 1 }\n      })\n  })\n}\n} catch (e) {\n  throw(JSON.stringify(user))\n}\n\ncontent = org.jsoup.Jsoup.parse(java.t2s(ajax(source.bookSourceUrl + \"/wenku/\")))\ncontent.select(\"ul#filters li.sort-li\").forEach(li => {\n  res.push({\n    title: `>> ${li.select(\"h3\").textNodes()[0].text()} <<`,\n    url: \"\",\n    style: {\n      layout_flexGrow: 1,\n      layout_flexBasisPercent: 1\n    }\n  })\n  li.select(\"a\").forEach(a => {\n    let filter = {}\n    let type = a.attr(\"data-filter-type\")\n    let value = a.attr(\"data-filter-value\")\n    filter[type]=value\n    res.push({\n      title: a.text(),\n      url: generateFilterUrl(filter),\n      style: { layout_flexGrow: 1 }\n    })\n  })\n})\nJSON.stringify(res)",
    "header": "{\n\"Referer\":\"https://w.linovelib.com/\",\n\"User-Agent\":\"\",\n\"Accept-Language\":\"zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6\",\n\"Accept\":\"*/*\"\n}",
    "jsLib": "function errorReg() {\n  return /(java.net.+Exception)|(okhttp.+Exception)|(error code: 1015)|(cf-error[\\s\\S]*1015)|(503 Service Temporarily Unavailable)|(403 Forbidden)|(Parse error.+in.+wwwroot.+php)|(_cf_chl_opt|challenges.css)|(<center.+>.+不支持)|(章节内容不支持该浏览器显示)|(内容加载失败！)/gi\n}\nfunction ajax(url) {\n  const { java, cookie } = this;\n  let content = \"\";\n  let time=1;\n  \tcontent = java.ajax(url);\n  \twhile (String(content).match(errorReg())) {\n    if (time>10) {\n  \t    java.toast(\"重试10次后仍请求失败：\"+url)\n  \t    throw(\"重试10次后仍请求失败：\"+url+content)\n  \t    break;\n    \t}\n    \tif (!!String(content).match(/403|1015/gi)) {\n    \t  const nowTime = new Date().getTime()\n    \t  const waitTime = (Math.random() + 1) * time * 10\n    \t  java.toast(`访问频率过高，等待 ${waitTime} 秒`)\n    \t  while (new Date().getTime() < nowTime + waitTime * 1000) {}\n    \t}\n    \tif (!!String(content).match(/不支持/gi)) {\n      //java.log(cookie.getCookie(url));\n      cookie.replaceCookie(url,java.webView(null,url,\"document.cookie\"));\n    \t}\n    \tif (!!String(content).match(/_cf_chl_opt|challenges.css|403/gi)) {\n    \t\tlet i = 1;\n      \twhile (!!String(content).match(/_cf_chl_opt|challenges.css|403/gi)) {\n\t       java.log('盾');\n\t       java.log(content);\n        if (i <= 1) {\n          java.toast(\"哦呼，五秒盾，正在尝试静默破盾ing……\");\n          content = java.webView(null,url,null);\n        } else {\n          java.toast(\"啊哦，静默破盾好像不管用，试试手动过校验吧！\");\n          content = java.startBrowserAwait(url,\"加载完毕后点完成，此页面可能会弹出多次\").body();\n        }\n        i++;\n      }\n      continue;\n    }\n  \t\t java.log(\"请求失败：\"+url)\n  \t\t java.log(\"重试\"+time+\"次\")\n  \t\t let t=new Date().getTime()\n    while (new Date().getTime() - t < 500) { }\n    if (String(content).match(/1015|503|php/gi)) {\n    \t  while (new Date().getTime() - t < 5000) { }\n    \t}\n  \t\t content = java.ajax(url);\n  \t\t time++;\n  \t}\n  \treturn String(content);\n}",
    "lastUpdateTime": 1773586496732,
    "loginUi": "[\n  {\n    \"name\": \"账号\",\n    \"type\": \"text\"\n  },\n  {\n    \"name\": \"密码\",\n    \"type\": \"password\"\n  }\n]",
    "loginUrl": "@js:\nfunction login() {\n  let une = source.getLoginInfoMap().get(\"账号\")\n  let pwd = source.getLoginInfoMap().get(\"密码\")\n  if (une && pwd) {\n    let body = String('username=' + une + '&password=' + pwd + '&usecookie=315360000&action=login&submit=')\n    let url = 'https://tw.linovelib.com/login.php'\n    ajax(url)\n    let ck = java.post(url, body, {\n    \t \"Content-Type\": \"application/x-www-form-urlencoded\",\n    \t \"User-Agent\": java.getWebViewUA()}).cookies()\n    let header = JSON.stringify({\n      \"Cookie\": String(ck).match(/\\{(.*?)\\}/)[1].replace(/,/g, ';')\n    })\n    source.putLoginHeader(header)\n  }\n}",
    "respondTime": 180000,
    "ruleBookInfo": {
      "author": "[property=\"og:novel:author\"]@content\n@js:\njava.t2s(result)",
      "canReName": "1",
      "coverUrl": ".book-cover@src",
      "intro": "标签：{{@@class.tag-small red@text##\\s## - }}\n{{@@id.bookSummary@tag.content@textNodes}}\n@js:\njava.t2s(result)",
      "kind": "{{@@class.book-cell@tag.p.1@text##\\|##,}}\n{{@@class.tag-small red@text}}\n##.*萬字\\s*|·.*\n@js:\nres=[]\nresult.split(/[,\\n]/).forEach(item=>res.push(java.t2s(item)))\nres",
      "lastChapter": "class.gray ell@text##(\\d+-\\d+-\\d+\\s\\d+:\\d+)·(.*)##$2 • $1\n@js:\njava.t2s(result)",
      "name": "[property=\"og:novel:book_name\"]@content\n@js:\njava.t2s(result)",
      "tocUrl": "class.btn-normal red@href",
      "wordCount": "class.book-cell@tag.p.1@text##\\s*\\|.*\n@js:\njava.t2s(result)"
    },
    "ruleContent": {
      "content": "id.acontent@html\n@js:\nif (String(src).match(errorReg())) {\n  java.log(src)\n  src=ajax(baseUrl);\n  java.setContent(src);\n  result=java.getString(\"id.acontent@html\")\n}\njava.t2s(result.replace(/img1.readpai.com/gi,'img3.readpai.com'))",
      "imageStyle": "FULL",
      "nextContentUrl": "##url_next:'([^']*)'##$1###\n@js:\n//121_2.html这样的是下一页，纯数字则是下一章\n//带有catalog是详情页\nvar isNew=/(\\/(\\d+).html)|catalog/.test(result);\nvar out=isNew?'':result;\nout",
      "replaceRegex": "##((?<=[\\u4e00-\\u9fa5“‘「（，])\\s+)?<!--\\s*\\（继续下一页\\）\\s*-->\\s*|((?<=[\\u4e00-\\u9fa5“‘「（，])\\s+)?\\（本章未完\\）\\s*|",
      "title": "id.atitle@text\n@js:\njava.t2s(result)"
    },
    "ruleExplore": {
      "author": "class.book-author@ownText\n@js:\njava.t2s(result)",
      "bookList": "@js:\ni = 1;\njava.log(java.getString(\"html\"));\n//java.log(java.startBrowserAwait(baseUrl,\"加载完毕后点完成，此页面可能会弹出多次\").body());\nwhile (!!String(java.getString(\"html\")).match(/_cf_chl_opt|challenges.css/gi)) {\n\t java.log('盾');\n\t java.log(java.getString(\"html\"));\n  if (i <= 1) {\n    java.toast(\"哦呼，五秒盾，正在尝试静默破盾ing……\");\n    java.setContent(java.webView(null,baseUrl,null));\n  } else {\n    java.toast(\"啊哦，静默破盾好像不管用，试试手动过校验吧！\");\n    java.setContent(java.startBrowserAwait(baseUrl,\"加载完毕后点完成，此页面可能会弹出多次\").body());\n  }\n  i++;\n}\njava.log(String(java.getString(\"html\")).match(/_cf_chl_opt|challenges[.]css/gi));\njava.log(String(java.getString(\"html\")));\nresult = java.getElement(\"class.book-ol@tag.li\");\nresult;",
      "bookUrl": "a@href",
      "coverUrl": "img@data-src",
      "intro": "class.book-desc@text&&class.ell@text\n@js:\njava.t2s(result)",
      "kind": "class.tag-small-group origin-right@tag.em.0@text&&\nclass.tag-small-group origin-right@tag.em.1@text&&\ntag.time@text\n@js:\nres=[]\nresult.forEach(item=>{\n    item.split(new RegExp(\"[ \\/]\",\"gi\")).forEach(i=>res.push(java.t2s(i)))\n})\nres",
      "name": "class.book-li@tag.img@alt\n@js:\njava.t2s(result)",
      "wordCount": "class.tag-small blue@text\n@js:\njava.t2s(result)"
    },
    "ruleSearch": {
      "bookList": "class.gsc-expansionArea@class.gsc-webResult gsc-result\n@js:\nres=[]\nresult.forEach(item=>{\n  java.setContent(item)\n  if (String(java.getString(\"class.gs-title.0@tag.a.0@href\")).match(/novel\\/\\d+\\.html/gi)) {\n    res.push(item)\n  }\n})\nres",
      "bookUrl": "tag.a.0@href",
      "checkKeyWord": "我的青春恋爱喜剧",
      "coverUrl": "a@href@js:\nvar id = result.match(/\\/(\\d+)\\.html/)[1];\n'https://www.linovelib.com/files/article/image/'+parseInt(id/1000)+'/'+id+'/'+id+'s.jpg';",
      "intro": "class.gs-bidi-start-align gs-snippet@text\n@js:\njava.t2s(result)",
      "name": "class.gs-title.0@tag.a.0@text##線上看\\|epub,txt下載_嗶哩輕小說\n@js:\njava.t2s(result)"
    },
    "ruleToc": {
      "chapterList": "@js:\nif (String(src).match(errorReg())) {\n  src=ajax(baseUrl);\n}\njava.setContent(src)\nresult = java.getElement(\"id.catelogX@.chapter-li:not(.volume-cover)\");\n    //现实debug(尝试修复正文链接问题，和目录不全)\n    //《好友角色的我怎么可能大受欢迎》第三卷12\n    //《我的青春恋爱喜剧》\n    //2022-8-19\n    //原来的代码在源注释（已移除）\n    //2022-8-20修复https://w.linovelib.com/novel/2765.html目录加载失败\n    //2023-9-30使用易于理解的变量命名\n    //2023-10-7处理cid(1)以及连续多个cid(0)\n    //2023-11-11解决了一个原来手滑写出的bug\n    //2023-11-16处理连续多个卷名\n    //2023-12-12适配新版卷名\n    //2024-2-6处理《谁说从妥协开始的恋爱一定没结果》\n\nres = result\n//java.log(res)\n\nfor (i = 0; i < res.length; i++) {\n    java.setContent(res[i])\n    if (java.getString(\"tag.a@href\").match(/javascript:cid\\(.+\\)/gi)) {\n        if (String(res[(i == res.length - 1?i:i+1)]).match(/javascript:cid\\(.+\\)/gi)) {\n            java.setContent(res[i - 1])\n            prevLink = java.getString(\"a:not(:has(h3))@href\")\n            if (prevLink == \"\") {\n                java.setContent(res[i - 2])\n                prevLink = java.getString(\"tag.a@href\")\n            }\n            content = ajax(source.bookSourceUrl + prevLink)\n            java.setContent(content)\n            foot = java.getElements(\"id.footlink\")\n            path = prevLink\n\n            //java.log(foot)\n            while (String(foot).match(/下一页|下一頁/gi)) {\n            \t    //java.log(foot)\n                str = content.match(/<script type\\=\\\"text\\/javascript\\\">var ReadParams.*/)\n                path = String(str).match(/url_next\\:'.*?html/)[0].replace(\"url_next:'\", \"\")\n                content = java.ajax(\"https://w.linovelib.com\" + path)\n                java.setContent(content)\n                foot = java.getElements(\"id.footlink\")\n            }\n\n            next = ajax(source.bookSourceUrl + path)\n            str = next.match(/<script type\\=\\\"text\\/javascript\\\">var ReadParams.*/)\n            path = String(str).match(/url_next\\:'.*?html/)[0].replace(\"url_next:'\", \"\")\n            java.log('l:'+path)\n            res[i] = String(res[i]).replace(/javascript:cid\\(.+\\)/gi, path)\n        } else {\n            nextLink = \"\"\n            for (var j=1;nextLink==\"\";j++) {\n                java.setContent(res[i + j])\n                nextLink = java.getString(\"a:not(:has(h3))@href\")\n            }\n            next = ajax(source.bookSourceUrl + nextLink)\n            str = next.match(/<script type\\=\\\"text\\/javascript\\\">var ReadParams.*/)\n            try {\n              path = String(str).match(/url_previous\\:'.*?html/)[0].replace(\"url_previous:'\", \"\")\n            } catch(e) {\n            \t  java.toast(String(next)+\"\\n\")\n            \t  java.log(next)\n            \t  throw(\"目录解析报错开始：\\n\"+String(next)+\"\\n结束\")\n            \t}\n            //java.log(path)\n\n            res[i] = String(res[i]).replace(/javascript:cid\\(.+\\)/gi, path)\n        }\n    }\n\n    //java.log(res[i])\n}\nres",
      "chapterName": "text\n@js:\njava.t2s(result)",
      "chapterUrl": "@js:\n//只有不为卷名时返回URL，避免阅读自动合并\njava.getString(\"class.chapter-bar@text\")!=java.getString(\"text\") ? java.getString(\"tag.a@href\") : \"\"",
      "isVolume": "@js:\njava.getString(\"class.chapter-bar@text\")==java.getString(\"text\")"
    },
    "searchUrl": "https://cse.google.com.hk/cse?cx=649de34f5e63448cb#gsc.tab=0&gsc.q={{key}}&gsc.sort=&gsc.page={{page}},{webView:true}",
    "weight": 0
  }
]