💫悦恋免费小说 - [☆星眠☆]

情不知所起一往而深, 情不知所终一笑而泯。 ––––––––––––––––@星眠

x5201314l (7559)3天前

星眠

欢迎入群:1070942328

禁止使用抓包,VPN

Update:
V2.30 => 2026.1.25:修复一些前后端bug


默认搜索 小说
支持搜索时快速切换
加上前缀:
听书:t:
漫画:m:
短剧:d:
例如:t:系统
二维码导入
{
    "bookSourceComment": "星眠\n\n欢迎入群:1070942328\n\n禁止使用抓包,VPN\n\nUpdate:\nV2.30 => 2026.1.25:修复一些前后端bug\n\n\n默认搜索 小说\n支持搜索时快速切换\n加上前缀:\n听书:t:\n漫画:m:\n短剧:d:\n例如:t:系统",
    "bookSourceGroup": "☃ 自用☃️",
    "bookSourceName": "💫悦恋免费小说 - [☆星眠☆]",
    "bookSourceType": 0,
    "bookSourceUrl": "情不知所起一往而深,\n情不知所终一笑而泯。\n––––––––––––––––@星眠",
    "customOrder": 0,
    "enabled": true,
    "enabledCookieJar": true,
    "enabledExplore": true,
    "exploreUrl": "@js:\n个人中心 = 1\n\njs = (cid, genre, gender) => `@js:\nxGorgon(\n  \"new_category\/landing\",\n  [\n    \"category_id=${cid}\",\n    \"limit=20\",\n    \"offset=\" + (page - 1),\n    \"gender=${gender}\",\n    \"genre=${genre}\"\n  ].join(\"&\")\n)`\n\ntt_rank_books = (url) => `@js:\n  let ck = \"sessionid=\" + (String(cookie.getKey(\"fanqienovel.com\", \"sessionid\")) ? String(cookie.getKey(\"fanqienovel.com\", \"sessionid\")) : (source. getLoginInfoMap() || {})['手动登录Token'])\n  let list = JSON.parse(java.ajax('${url}')).data.result\n  let book_ids = []\n  for (let i of list) book_ids.push(i.book_id)\n  xGorgon(\n    \"multi-detail\",\n    \"book_id=\" + book_ids.slice(0, 100).join(','),\n    null,\n    ck,\n  )\n`\n\nobj = (title, url, type, type1) => ({\n  title: title,\n  url: url,\n  style: {\n    layout_flexGrow: 1,\n    layout_flexBasisPercent: type\n  }\n})\n\nlet toutiao_rank_info = JSON.parse(java.ajax('https:\/\/novel.snssdk.com\/api\/novel\/channel\/homepage\/rank\/rank_list\/v2\/?need_type=1&offset=0&side_type=10&type=1&aid=1319') || '{\"data\":{\"type\":{\"type\":[]}}}').data.type.type\ntoutiao_rank = (index) => {\n  let rank_list = toutiao_rank_info[index] || []\n  let ret = []\n  if (rank_list) {\n    ret.push(obj(i.name + '榜', '', 1))\n    for (let j of rank_list.rank_list) {\n      ret.push(obj(j.name, 'https'))\n    }\n  }\n}\n\narr = []\njava.longToast(\"请稍等,发现列表正在热更新!\")\nindex=[0, 1, 2]\nfor (let i of index) {\n  \/\/ java.toast(i)\n  let type = i\n  $ = JSON.parse(java.ajax(xGorgon(\n    \"new_category\/front\",\n    [\n      \"update_version_code=58932\",\n      \"distinct_style=1\",\n      \"new_category_tab=\" + i\n    ].join(\"&\")\n  ))).data.category_tab_data\n  cate = []\n  cate.push(obj(\n    \"====== \" + $.tab_name + \" ======\", \"\", 1\n  ))\n  $.cell_data.forEach((c) => {\n    gender_1 = (type == 0 || type == 1) ? type : 1;\n    genre_1 = type == 3 ? 1 : type == 5 ? 110 : type == 6 ? 130 : 0;\n    cate.push(obj(\n      c.cell_name,\n      js(c.atom_data[0].category_data.category_id, genre_1, gender_1),\n      1\n    ))\n    for (j = 2; j < 5; j++) {\n      c.atom_data.slice(1).forEach((a) => {\n        d = a.category_data\n        if (d.name.length == j) {\n          cate.push(obj(d.name, js(d.category_id, genre_1, gender_1), -1))\n        }\n      })\n    }\n  })\n  arr = cate.concat(arr)\n}\n\nget = (path) => `@js:\nlet ck = \"sessionid=\" + (String(cookie.getKey(\"fanqienovel.com\", \"sessionid\")) ? String(cookie.getKey(\"fanqienovel.com\", \"sessionid\")) : (source.getLoginInfoMap() || {})['手动登录Token'])\n\nlet v = xGorgon(\n    \"${path}\",\n    \"offset=\" + (page - 1) * 20,\n    null,\n    ck,\n)\n\nxGorgon(\n  \"multi-detail\",\n  \"book_id=\" + getBookId(java.ajax(v)),\n  null,\n  ck,\n)`\n\nlet book_shelf_url = 'https:\/\/fanqienovel.com\/reading\/bookapi\/bookshelf\/info\/v:version\/?aid=1967&iid=0&version_code=57700&update_version_code=57700'\nlet book_shelf_info = JSON.parse(java.ajax(book_shelf_url + ', ' + JSON.stringify({\n  headers: {\n    'Cookie': \"sessionid=\" + (String(cookie.getKey(\"fanqienovel.com\", \"sessionid\")) ? String(cookie.getKey(\"fanqienovel.com\", \"sessionid\")) : (source.getLoginInfoMap() || {})['手动登录Token']),\n  }\n})))\n\njava.log(JSON.stringify(book_shelf_info))\n\nlet username\nif (book_shelf_info.code != 0) {\n  个人中心 = 0\n} else {\n  let uinfo = java.ajax(\"https:\/\/fanqienovel.com\/api\/user\/info\/v2,\" + JSON.stringify({ headers: { Cookie: \"sessionid=\" + (String(cookie.getKey(\"fanqienovel.com\", \"sessionid\")) ? String(cookie.getKey(\"fanqienovel.com\", \"sessionid\")) : (source.getLoginInfoMap() || {})['手动登录Token']) } }))\n  uinfo = JSON.parse(uinfo)\n  username = uinfo.data.name\n}\n\nlet gro = []\npush = (title, url, type) => gro.push({\n  title: title,\n  url: url,\n  style: {\n    layout_flexGrow: 1,\n    layout_flexBasisPercent: type\n  }\n});\n\nlet sArr = []\n\nif (个人中心) {\n  let groups_bookids = {\n    \"未分组\": []\n  }\n  book_shelf_info.data.book_shelf_info.forEach(i => {\n    if (!groups_bookids[i.group_name ? i.group_name : \"未分组\"]) groups_bookids[i.group_name] = []\n    groups_bookids[i.group_name ? i.group_name : \"未分组\"].push(i.book_id)\n  })\n\n  Object.keys(groups_bookids).forEach(k => {\n    var multi_detail_post_body = {\n      data: {\n        book_shelf_info: []\n      }\n    }\n    groups_bookids[k].forEach(i => multi_detail_post_body.data.book_shelf_info.push({ book_id: String(i), item_id: '0' }))\n    \/\/ var multi_detail_group_url = `https:\/\/fanqienovel.com\/api\/bookshelf\/multidetail,${JSON.stringify(multi_detail_post_body)}`\n    \/\/ var book_ids = getBookId(JSON.stringify(multi_detail_post_body)).join(\",\")\n    push(k, \"https:\/\/fanqienovel.com\/fqbookshelf\/groupName\/\" + k, 0.4)\n  })\n  if (Object.keys(groups_bookids).length % 2 != 0) push(\"占位\", \"\", 0.4)\n  \/\/ java.log(JSON.stringify(gro, null, 2))\n  sArr.push(obj(username + '的个人中心', '', 1))\n  sArr.push(obj('首页推荐', \"https:\/\/fanqienovel.com\/tab\/0\", 0.4))\n  sArr.push(obj('猜你喜欢', \"https:\/\/fanqienovel.com\/tab\/2\", 0.4))\n  \/\/ sArr.push(obj('猜你喜欢', \"https:\/\/fanqienovel.com\/tab\/1\", 0.4))\n  sArr.push(obj(\"我的书架\", \"https:\/\/fanqienovel.com\/fqbookshelf\", 1))\n  \n  sArr = sArr.concat(gro)\n  \/\/ java.toast(JSON.stringify(sArr)) \n  sArr.push(obj(\"阅读历史\", get(\"read_history\/list\"), 1))\n}\n\narr = sArr.concat(arr)\nJSON.stringify(arr)\n",
    "header": "<js>\n let defaultVariable = {\n \t    \"source\": {\n \t    \t    \"tab\": 1,\n \t    \t    \"mode\": \"📖小说\",\n \t    \t    \"source\": \"番茄小说\"\n \t    \t},\n \t    \t\"studio\": {\n \t    \t\t    \"num\": \"\",\n \t    \t\t    \"toneId\": \"\",\n \t    \t\t    \"toneName\": \"\"\n \t    \t\t},\n \t    \t\t\"version\": version,\n \t    \t\t\"Cookie\": cookie.getKey(\"fanqienovel.com\", \"sessionid\")||\"\",\n \t    \t\t\"progress\": false,\n \t    \t\t\"apiIndex\": 0,\n \t    \t\t\"register\": false,\n \t    \t\t\"bookshelfSync\": false,\n \t    \t\t\"bookComment\": false,\n \t    \t\t\"lastSyncProgressTime\": Date.now(),\n \t    \t\t\"bookshelf\": [\n \t    \t\t    {\n \t    \t\t    \t    \"bookId\": \"\" \n \t    \t\t    \t}\n \t    \t\t ]\n \t}\n let variable = source.getVariable();\n if(variable != \"\" && variable != null){\n   try{\n      variable = JSON.parse(variable);\n   } catch(err) {\n     java.log(err);\n     variable = defaultVariable;\n     source.variable = JSON.stringify(defaultVariable);\n     java.toast(\"请不要自己源变量,已重置源变量!\")\n   }\n } else {\n   variable = defaultVariable;\n   java.toast(\"已进行书源配置初始化!\");\n   source.variable = JSON.stringify(defaultVariable);\n  }\n let Header = {\n \t    \"Version\": version,\n \t    \"Device\": java.androidId(),\n \t    \"X-Novel-Type\": \"read.yuedu\",\n     \"Key\": source.getLoginInfoMap().get(\"你的专属Key\") || \"\"\n     }\n  JSON.stringify(Header)\n <\/js>",
    "jsLib": "let replaceCover = (u) => {\n    if (u.startsWith(\"https:\/\/\")) u = u.substring(8)\n    else u = u.substring(7)\n    let uArr = u.split(\"\/\")\n    uArr[0] = \"https:\/\/p6-novel.byteimg.com\/origin\"\n    let uArr2 = []\n    uArr.forEach((x) => {\n        if (!x.includes(\"?\") && !x.includes(\"~\")) uArr2.push(x)\n        else uArr2.push(x.split(\"~\")[0])\n    })\n    u = uArr2.join(\"\/\")\n    return u\n}\n\nconst action = \"\/fanqienovel\/api\/\";\n\nconst version = \"2.30\";\n\nconst api = [\n    \"https:\/\/rose.read.xingmian.icu\",\n    \"https:\/\/rose.read.lzink.icu\",\n    \"http:\/\/154.12.87.33:443\"\n]\nfunction timestamp() {\n    let time = Date.now();\n    let str = time.toString();\n    let num = str.slice(0, 10);\n    \/\/let timestamp = Number(num);\n    return num\n}\n\nfunction getComic(result) {\n  let mat = result.match(\/<article>([\\s\\S]*?)<\\\/article>\/);\n  try {\n    let cnt = JSON.parse(\n      mat\n        ? mat[1].replace(\/\\&\/g, '\"').replace(\/\\;\/g, \"\").replace(\/\\#34\/g, \"\")\n        : result\n    );\n    return (mat ? cnt.skeleton.data : cnt.picInfos)\n      .map((i) => {\n        let path = mat\n          ? cnt.materials[i.element_name].data.web_uri\n          : \"novel-pic\/\" + i.md5;\n        return `<img src=\"https:\/\/p3-novel.byteimg.com\/origin\/${path}\">`;\n      })\n      .join(\"<br>\");\n  } catch (e) { \/\/ not comic content\n    mat = result.match(\/<body>([\\s\\S]*?)<\\\/body>\/)\n    \/\/ java.log(result)\n    return (mat ? mat[1] : result).toString().replace(\/\\<\\!DOCTYPE html.*\/g, \"\").replace(\/\\<tt_keyword_ad.*\\<\\\/tt_keyword_ad\\>\/, \"\").replace(\/\\<a epub.*\\>\\<\\\/a\\>\/g, \"\")\n  }\n}\n\nlet Host = \"https:\/\/fanqienovel.com\"\n\nlet wedapi = Host + \"\/reading\/bookapi\/bookshelf\/add\/v:version\";\n\nlet reurl = Host + \"\/api\/reader\/book\/update_progress\";\n\ngetHost = (a, b, c, d) => [\n    [\"https:\/\/\"][0],\n    [\n        \"reading\",\n        \"api\",\n        \"api3\",\n        \"api5\",\n        \"novel\",\n        \"\",\n    ][(a == 4 ? 5 : b) || 0],\n    [\n        \"\",\n        \"-normal\",\n    ][c || 0],\n    [\n        \"\",\n        \"-hl\",\n        \"-lf\",\n        \"-lq\",\n        \"-sinfonlinea\",\n        \"-sinfonlineb\",\n        \"-sinfonlinec\",\n    ][d || 0],\n    [\".\", \"\"][a == 4 ? 1 : 0],\n    [\n        \"snssdk\",\n        \"fqnovel\",\n        \"fanqiesdk\",\n        \"toutiaoapi\",\n        \"fanqienovel\",\n    ][a || 0],\n    [\".com\"][0],\n].join(\"\");\njavaImport = new JavaImporter()\njavaImport.importPackage(\n    Packages.okhttp3,\n    Packages.cn.hutool.core.util,\n    Packages.cn.hutool.core.codec,\n    Packages.cn.hutool.crypto.digest\n)\n\nfunction gzip(data) { ZipUtil.gzip(data, \"\") }\n\nfunction xGorgon(path, params, data, ck) {\n    const { java, source } = this;\n    params = [\n        params,\n        \"aid=1967\",\n        \"channel=0\",\n        \"os_version=0\",\n        \"app_name=novelapp\",\n        \"version_code=58932\",\n        \"device_platform=android\",\n        \"device_type=unknown\",\n    ].join(\"&\").split(\"&\").sort().join(\"&\").replace(\/^&+\/, \"\");\n    \n    if (!data) {\n        path = \"\/reading\/bookapi\/\" + path + \"\/v\/?\";\n    }\n    \n    let url = getHost() + path + params;\n    let devtype;\n    for (let i of (source.getLoginHeader() || '').split('&')) {\n        if (i.startsWith('device_type')) {\n            devtype = i.split('=')[1];\n        }\n    }\n    let md5 = (str) => javaImport.DigestUtil.md5Hex(str);\n    let rStr = (str) => javaImport.StrUtil.reverse(str);\n    let Hex = (num) => num.toString(16).padStart(2, \"0\");\n    let rHex = (num) => parseInt(rStr(Hex(num)), 16);\n    function rBin(num) {\n        let bin = num.toString(2).padStart(8, \"0\");\n        return parseInt(rStr(bin), 2);\n    }\n    function getHex(ck) {\n        let hex = md5(params);\n        hex += data ? md5(data) : \"0\".repeat(8);\n        hex += ck ? md5(ck) : \"0\".repeat(8);\n        return hex;\n    }\n\n    function calculate(hex, ck) {\n        let len = 0x14;\n        let key = [0xDF, 0x77, 0xB9, 0x40, 0xB9, 0x9B, 0x84, 0x83, 0xD1, 0xB9, 0xCB, 0xD1, 0xF7, 0xC2, 0xB9, 0x85, 0xC3, 0xD0, 0xFB, 0xC3];\n        let paramList = [];\n        \n        for (let i = 0; i < 9; i += 4) {\n            let temp = hex.substring(8 * i, 8 * (i + 1));\n            for (let j = 0; j < 4; j++) {\n                let h = parseInt(temp.substring(j * 2, (j + 1) * 2), 16);\n                paramList.push(h);\n            }\n        }\n        \n        paramList.push(0x0, 0x6, 0xB, 0x1C);\n        let T = Math.floor(Date.now() \/ 1000);\n        paramList.push((T >> 24) & 0xFF, (T >> 16) & 0xFF, (T >> 8) & 0xFF, T & 0xFF);\n        let eorResultList = [];\n        for (let i = 0; i < paramList.length; i++) {\n            eorResultList.push(paramList[i] ^ key[i % len]);\n        }\n        \n        for (let A, B, C, D, i = 0; i < len; i++) {\n            A = rHex(eorResultList[i]);\n            B = eorResultList[(i + 1) % len];\n            C = rBin(A ^ B);\n            D = ((C ^ 0xFFFFFFFF) ^ len) & 0xFF;\n            eorResultList[i] = D;\n        }\n        \n        let result = \"\";\n        for (let param of eorResultList) {\n            result += Hex(param);\n        }\n        \n        let option = {\n            \"headers\": {\n                \"X-Khronos\": String(T),\n                \"X-Gorgon\": \"0404b0d30000\" + result,\n                \"User-Agent\": 'com.dragon.read',\n                \"Cookie\": ck ? ck : \"\"\n            }\n        };\n        \n        if (data) {\n            let json = javaImport.MediaType.parse(\"application\/json\");\n            let request = new javaImport.Request.Builder()\n                .url(url)\n                .post(javaImport.RequestBody.create(data, json));\n                \n            for (let n in option.headers) {\n                request.addHeader(n, option.headers[n]);\n            }\n            \n            let client = new javaImport.OkHttpClient();\n            let response = client.newCall(request.build()).execute();\n            return JSON.parse(response.body().string()).data;\n        } else {\n            return url + \",\" + JSON.stringify(option);\n        }\n    }\n    \n    return calculate(getHex(ck), ck);\n}\nfunction getBookId(url) {\n    const {java} = this;\n    let $ = JSON.parse(url).data;\n    let arr;\n    \n    if ($.book_shelf_info && $.book_shelf_info.length > 0) {\n        arr = $.book_shelf_info.map($ => $.book_id);\n    } else if ($.data_list && $.data_list.length > 0) {\n        arr = $.data_list.map($ => $.book_id_str);\n    } else {\n        java.toast(\"获取 book_id 失败,你可能需要登录!\");\n        return [];\n    }\n    \n    return arr.slice(0, 100);\n}\n\nfunction getBookIdFull(url) {\n\tconst {java} = this\n\tlet $ = JSON.parse(url).data\n  let arr, list\n\tif ($.book_shelf_info != 0 && $.book_shelf_info != undefined) {\n\t\tarr = $.book_shelf_info.map($ => $.book_id)\n\t} else if (list = $.data_list != 0 && $.data_list != undefined)  {\n\t\tarr = $.data_list.map($ => $.book_id_str)\n\t} else {\n\t\tjava.toast(\"获取 book_id 失败,你可能需要登录!\")\n\t}\n\treturn arr\n}\n\nfunction splitArray(input, size) {\n    const output = [];\n    for (let i = 0; i < input.length; i += size) {\n        output.push(input.slice(i, i + size));\n    }\n    return output;\n}\n\n\/\/ 从此处开始即为段评支持\nfunction setend(text, bookId, itemId) {\n\t   const{java, source, cont} = this;\n\t   let bookComment = JSON.parse(source.variable).bookComment;\n\t   if(bookComment) {\n\t   \t    \/\/let cid = JSON.parse(cont).item_id;\n\t   \t    let content = text.replace(\/\\n+\/g,\"\\n\");\n\t   \t    content = this.getComments(content, bookId, itemId)\n\t   \t    return content;\n\t   \t } else {\n\t   \t \t   return text;\n\t   \t \t}\n}\nfunction getComments(content, bid, cid) {\n    let { java, cache, source } = this;\n    try {\n        \/\/ 填写评论多少的气泡\n        let apiUrl = `https:\/\/fanqie.acgkami.com\/comment_list.php?item_id=${cid}`;\n        let comments = java.ajax(apiUrl);\n        let lines = content.split(\"\\n\");\n        let raw = JSON.parse(comments).data.data;\n        Object.keys(raw).forEach((x) => {\n            if (x < lines.length) {\n                cache.putMemory(`fq-${bid}-${cid}-${x}-text`, lines[x]);\n                let color = \"red\";            \n                lines[x] += `<img src=\"${this.createSvg(raw[x].count, color, bid, cid, x)}\">`;\n            };\n        });\n\n        let chapterDiscussionSvg = this.createChapterDiscussionSvg(bid, cid);\n        lines.push(`<img src=\"${chapterDiscussionSvg}\">`);\n\n        return lines.join(\"\\n\");\n    } catch (e) {\n        return content;\n    }\n}\n\n\/\/ 自定义随机气泡数字\nfunction getraw(length) {\n    const { java, source } = this;\n    let result = {};\n    for (let i = 0; i < length; i++) {\n        result[i] = { count: Math.floor(Math.random() * 100) + 1 };\n    }\n    return result;\n}\n\nfunction createSvg(number, color, bid, cid, para) {\n    var displayText = number > 99 ? \"99+\" : number;\n    var date = String(Date.now()).match(\/(\\d{6}$)\/)[1];\n    var svg;\n    svg = '<svg width=\"1000\" height=\"909\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">' +\n          '<path d=\"M80,80 h840 a60,60 0 0 1 60,60 v580 a60,60 0 0 1 -60,60 h-620 l-140,90 v-90 h-80 a60,60 0 0 1 -60,-60 v-580 a60,60 0 0 1 60,-60 z\" ' +\n          'fill=\"none\" stroke=\"' + color + '\" stroke-width=\"16\" stroke-linejoin=\"round\"\/>' +\n          '<text x=\"500\" y=\"450\" font-family=\"Arial, sans-serif\" text-anchor=\"middle\" ' +\n          'font-size=\"360\" fill=\"' + color + '\" dy=\"0.3em\">' + displayText + '<\/text>' +\n          '<\/svg>';\n\n    var encodedSvg = this.java.base64Encode(svg);\n    return 'data:image\/svg+xml;base64,' + encodedSvg + ',{\"js\":\"showCmt(\\'' + bid + '\\',\\'' + cid + '\\',\\'' + para + '\\',\\'' + date + '\\')\",\"style\":\"text\"}';\n}\n\nfunction showCmt(bid, cid, para, date) {\n    let { java, cache, cookie, source } = this;\n    let mname = `fq-${bid}-${cid}-${para}`;\n    let load = (cache.getFromMemory(mname) ?? \"-\").split(\"-\");\n    \n    if (load[0] != \"1\" || load[1] != date) {\n        cache.putMemory(mname, \"1-\" + date);\n        \/\/java.toast(\"跳过加载\");\n        return;\n    }\n    \/\/ 评论显示的html \n    let apiUrl = `https:\/\/fanqie.acgkami.com\/comments.html?book_id=${bid}&item_id=${cid}&para_index=${para}`;\n    let title = cache.getFromMemory(mname + \"-text\") ?? \"段评内容\";\n    \n    java.startBrowser(apiUrl, title);\n}\n\nfunction createChapterDiscussionSvg(bid, cid) {\n    const displayText = \"本章讨论\";\n    const date = String(Date.now()).match(\/(\\d{6}$)\/)[1];\n    const svg = `<svg width=\"1000\" height=\"120\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\"><rect width=\"1000\" height=\"120\" fill=\"#A9A9A9\" rx=\"15\"\/><text x=\"120\" y=\"75\" font-family=\"Arial\" font-size=\"48\" fill=\"#000000\" text-anchor=\"middle\">${displayText}<\/text><text x=\"965\" y=\"75\" font-family=\"Arial\" font-size=\"36\" fill=\"#000000\" text-anchor=\"middle\">〉<\/text><\/svg>`;\n    const encodedSvg = this.java.base64Encode(svg);\n    return 'data:image\/svg+xml;base64,' + encodedSvg + ',{\"js\":\"showChapterDiscussion(\\'' + bid + '\\',\\'' + cid + '\\',\\'' + date + '\\')\",\"style\":\"full\"}';\n}\n\n\nfunction showChapterDiscussion(bid, cid, date) {\n    let { java, cache, cookie, source } = this;\n    let mname = `fq-${bid}-${cid}`;\n    let load = (cache.getFromMemory(mname) ?? \"-\").split(\"-\");\n    \n    if (load[0] != \"1\" || load[1] != date) {\n        cache.putMemory(mname, \"1-\" + date);\n        \/\/java.toast(\"跳过加载\");\n        return;\n    }\n    \/\/ 评论显示的html \n    let apiUrl = `https:\/\/fanqie.acgkami.com\/commentsfq.html?book_id=${bid}&item_id=${cid}`;\n    let title = cache.getFromMemory(mname + \"-text\") ?? \"章节讨论内容\";\n    \n    java.startBrowser(apiUrl, title);\n}\n\nconst commentapi = \"https:\/\/fanqie.acgkami.com\/commentsfq.html\";\n\nfunction register(type) {\n\t   const { java, source } = this;\n    let durl = api[0] + \"\/fanqienovel\/api\/register.php?action=refresh\";\n    if(!type) {\n    \t    durl = api[0] + \"\/fanqienovel\/api\/register.php?action=register\";\n    \t    let variable = JSON.parse(source.getVariable());\n    \t    variable.register = true;\n    \t    source.setVariable(JSON.stringify(variable));\n    \t}\n    let Header = JSON.stringify({\n    \t    headers: {\n    \t    \t    device: java.androidId()\n    \t    \t}\n    \t});\n    \tlet DEVICE = durl + \",\" + Header;\n    \/\/java.log(DEVICE);\n    java.ajax(DEVICE);\n}",
    "lastUpdateTime": "1769832316259",
    "loginUi": "[\n    {\n        \"name\": \"☆星眠书源设置☆\",\n        \"type\": \"button\",\n        \"action\": \"\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": 1\n        }\n    },\n    {\n        \"name\": \"🔑获取密钥Key\",\n        \"type\": \"button\",\n        \"action\": \"Turl(0)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },\n    {\n        \"name\": \"🔑找回密钥Key\",\n        \"type\": \"button\",\n        \"action\": \"Turl(1)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },\n    {\n        \"name\": \"🎂获取白名单\",\n        \"type\": \"button\",\n        \"action\": \"Turl(2)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },\n    {\n        \"name\": \"📡切换服务器\",\n        \"type\": \"button\",\n        \"action\": \"Sapi()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },\n    {\n        \"name\": \"🔭书源更新\",\n        \"type\": \"button\",\n        \"action\": \"Turl(3)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },\n    {\n        \"name\": \"🛰服务器检测\",\n        \"type\": \"button\",\n        \"action\": \"checkAPI()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },\n    {\n        \"name\": \"你的专属Key\",\n        \"type\": \"password\",\n        \"action\": \"\"\n    },\n    {\n        \"name\": \"🍅番茄小说设置🍅\",\n        \"type\": \"button\",\n        \"action\": \"\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": 1\n        }\n    },\n    {\n        \"name\": \" [    账号登录    ] \",\n        \"type\": \"button\",\n        \"action\": \"l2(true)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },\n    {\n        \"name\": \" [    退出登录    ] \",\n        \"type\": \"button\",\n        \"action\": \"logout()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },\n    {\n        \"name\": \" [    登录检查    ] \",\n        \"type\": \"button\",\n        \"action\": \"l2(false, true)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },\n    {\n        \"name\": \"✔番茄阅读进度同步\",\n        \"type\": \"button\",\n        \"action\": \"ProgressSync()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },\n    {\n        \"name\": \"📜段评设置\",\n        \"type\": \"button\",\n        \"action\": \"bookComment()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },\n    {\n        \"name\": \"📕推送至番茄书架\",\n        \"type\": \"button\",\n        \"action\": \"bookSelfSync()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },\n    {\n        \"name\": \"手动登录Token\",\n        \"type\": \"password\",\n        \"action\": \"\"\n    },\n    {\n        \"name\": \"🔎设置搜索来源🔍\",\n        \"type\": \"button\",\n        \"action\": \"\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": 1\n        }\n    },\n    {\n        \"name\": \"📖小说📖\",\n        \"type\": \"button\",\n        \"action\": \"setSource('xiaoshuo')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },\n    {\n        \"name\": \"🌄漫画🌄\",\n        \"type\": \"button\",\n        \"action\": \"setSource('manhua')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },\n    {\n        \"name\": \"💿听书💿\",\n        \"type\": \"button\",\n        \"action\": \"setSource('tinshu')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },\n    {\n        \"name\": \"📹短剧📹\",\n        \"type\": \"button\",\n        \"action\": \"setSource('duanju')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },\n    {\n        \"name\": \"💿设置听书音色💿\",\n        \"type\": \"button\",\n        \"action\": \"get()\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": 1\n        }\n    },\n    {\n        \"name\": \"多人对话\",\n        \"type\": \"button\",\n        \"action\": \"set(1)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"真人发音\",\n        \"type\": \"button\",\n        \"action\": \"set(2)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"甜美少女\",\n        \"type\": \"button\",\n        \"action\": \"set(3)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"清亮青叔\",\n        \"type\": \"button\",\n        \"action\": \"set(4)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"成熟大叔\",\n        \"type\": \"button\",\n        \"action\": \"set(5)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"开朗青年\",\n        \"type\": \"button\",\n        \"action\": \"set(6)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"温柔淑女\",\n        \"type\": \"button\",\n        \"action\": \"set(7)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"风雅青叔\",\n        \"type\": \"button\",\n        \"action\": \"set(8)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"清纯少女\",\n        \"type\": \"button\",\n        \"action\": \"set(9)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"磁性青叔\",\n        \"type\": \"button\",\n        \"action\": \"set(10)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"儒雅大叔\",\n        \"type\": \"button\",\n        \"action\": \"set(11)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"优雅御姐\",\n        \"type\": \"button\",\n        \"action\": \"set(12)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"斯文青叔\",\n        \"type\": \"button\",\n        \"action\": \"set(13)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"知性主播\",\n        \"type\": \"button\",\n        \"action\": \"set(14)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"成熟升级\",\n        \"type\": \"button\",\n        \"action\": \"set(15)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"多人升级\",\n        \"type\": \"button\",\n        \"action\": \"set(16)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"俏皮御姐\",\n        \"type\": \"button\",\n        \"action\": \"set(17)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"双音灵动\",\n        \"type\": \"button\",\n        \"action\": \"set(18)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"  接    口:星眠     源规则: 星眠  \",\n        \"type\": \"button\",\n        \"action\": \"author()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 1.5\n        }\n    }\n]",
    "loginUrl": "\/\/ 登录及登录检查\nfunction login_(openBrowser, checkMode) {\n    \/\/ java.removeCookie(\"snssdk.com\")\n    var cookie_ = String(cookie.getKey(\"fanqienovel.com\", \"sessionid\")) ? String(cookie.getKey(\"fanqienovel.com\", \"sessionid\")) : (source.getLoginInfoMap() ? source.getLoginInfoMap() : {})['手动登录Token']\n    \/\/ java.toast(cookie_)\n    if (cookie_ && cookie_ != \"\" && !checkMode) {\n        if (!source.variableComment) {\n            java.toast(\"请不要重复登录,请先退出登录!\")\n        } else {\n            java.toast(\"您为填写token登录,请手动移除token后再次登录\")\n        }\n        return false\n    }\n    if (openBrowser) {\n        try {\n            java.startBrowserAwait(\"https:\/\/fanqienovel.com\", \"登录\")\n        } catch (e) {\n            java.toast(e)\n        }\n    }\n    \/\/ java.log(\"snssdk cookie: \" + java.getCookie(\"snssdk.com\") + \"will be reomved\")\n    try {\n        cookie.removeCookie(\"snssdk.com\")\n    } catch (e) {}\n    \/\/ java.log(cookie)\n    var cookie_ = \"sessionid=\" + String(cookie.getKey(\"fanqienovel.com\", \"sessionid\")) ? String(cookie.getKey(\"fanqienovel.com\", \"sessionid\")) : (source.getLoginInfoMap())['手动登录Token']\n    let user\n    try {\n      user = JSON.parse(java.ajax(\"https:\/\/fanqienovel.com\/api\/user\/info\/v2,\" + JSON.stringify({\n        method: \"GET\",\n        headers: {\n          \"Cookie\": cookie_\n        }\n      }))).data.name\n     } catch (e) {java.log(e)}\n    if (!cookie_ || cookie_ == \"sessionid=\" || !user) {\n        java.toast(\"未获取到登录凭据,登录失败\")\n        return false\n    }\n    java.toast(\"欢迎 \" + user + \"\\n登录成功!\");\n    java.log(cookie_);\n    return true\n}\n\nfunction login() {\n\t\/\/ 一定程度上加上这个函数能够支持更多的版本\n\t}\n\nfunction l2(a, b) {\n  try {\n    login_(a, b)\n  } catch (e) {\n    java.log(e+\"\\n\"+e.stack)\n  }\n}\n\n\/\/ 取消登录\nfunction logout() {\n    cookie.removeCookie(\"fanqienovel.com\");\n    cookie.removeCookie(\"snssdk.com\");\n    if (String(cookie.getKey(\"fanqienovel.com\", \"sessionid\")) ? String(cookie.getKey(\"fanqienovel.com\", \"sessionid\")) : (source.getLoginInfoMap() ? source.getLoginInfoMap() : {})['手动登录Token']) java.toast(\"请手动移除填写的Token\")\n    else java.toast(\"退出登录成功\");\n}\n\nfunction setSource(smode) {\n    const map = { \n    \t    xiaoshuo: \n            [1, \"📖小说\"], \n        manhua: \n            [2, \"🌄漫画\"], \n        tinshu: \n            [3, \"💿听书\"], \n        duanju: \n            [4, \"📹短剧\"] \n        };\n    const [tab, mode] = map[smode] || [];\n    const variable = JSON.parse(source.getVariable());\n    variable.source.tab = tab;\n    variable.source.mode = mode;\n    source.setVariable(JSON.stringify(variable));\n    java.toast(`\\n已设置来源\\n${mode}`);\n}\n\nvar data = [\n    [\"阅读模式\", \"\"],\n    [\"多人对话\", \"tone_id=51\"],\n    [\"真人发音\", \"tone_id=0\"],\n    [\"甜美少女\", \"tone_id=1\"],\n    [\"清亮青叔\", \"tone_id=2\"],\n    [\"成熟大叔\", \"tone_id=4\"],\n    [\"开朗青年\", \"tone_id=5\"],\n    [\"温柔淑女\", \"tone_id=6\"],\n    [\"风雅青叔\", \"tone_id=8\"],\n    [\"清纯少女\", \"tone_id=12\"],\n    [\"磁性青叔\", \"tone_id=17\"],\n    [\"儒雅大叔\", \"tone_id=29\"],\n    [\"优雅御姐\", \"tone_id=30\"],\n    [\"斯文青叔\", \"tone_id=31\"],\n    [\"知性主播\", \"tone_id=32\"],\n    [\"成熟升级\", \"tone_id=74\"],\n    [\"多人升级\", \"tone_id=80\"],\n    [\"俏皮御姐\", \"tone_id=100\"],\n    [\"双音灵动\", \"tone_id=103\"]\n];\n\n\/\/ 设置保存接口\nfunction set(num) {\n    if (num < 0 || num >= data.length) {\n        java.longToast(\"无效的接口编号!\");\n        return;\n    }\n    var show = \"设置成功\\n当前模式:\";\n    var tips = \"\\n‼️设置完成请刷新详情页‼️\"\n    var setData = JSON.parse(source.getVariable());\n    setData.studio.toneName = data[num][0];\n    setData.studio.toneId = data[num][1];\n    setData.studio.num = num;\n    setData.studio.mode = \"💿听书\";\n    setData.source.tab = 3;\n    source.setVariable(JSON.stringify(setData));\n    var msg = show + data[num][0] + tips;\n    java.longToast(msg);\n}\nfunction get() {\n    var variable = source.getVariable();\n    try {\n        var num = JSON.parse(variable).studio.num;\n        if (num < 0 || num >= data.length) {\n            num = 0;\n        }\n        var show = \"当前模式:\";\n        java.longToast(show + data[num][0]);\n    } catch (e) {\n        java.longToast(\"获取模式失败,使用默认模式。\");\n    }\n}\n\nfunction Turl(num) {\n\t   let variable = JSON.parse(source.getVariable());\n\t   let apiIndex = variable.apiIndex;\n    let action = \"\/register\";\n    if(num == 1) {\n    \t    action = action + \"\/retrieve_key.php\";\n    \t} else if (num == 2) {\n    \t\t   action = action + \"\/donate.php\";\n    \t} else if(num == 3) {\n    \t    action = \"\/update?version=\" + version;\n    \t    source.setVariable(\"\");\n    \t}\n    java.startBrowserAwait(api[apiIndex] + action, \"星眠\");\n}\n\nfunction Sapi() {\n\t   let variable = JSON.parse(source.getVariable());\n\t   let apiIndex = variable.apiIndex;\n\t   if(apiIndex == 2) {\n\t   \t    variable.apiIndex = 0;\n\t   \t    apiIndex = 0;\n\t   \t} else {\n\t   \t\t   apiIndex = apiIndex + 1;\n\t   \t\t   variable.apiIndex = apiIndex;\n\t   \t}\n\t   \tsource.setVariable(JSON.stringify(variable));\n\t   \tjava.toast(\"服务器已切换至:\\n\" + api[apiIndex]);\n}\n\nfunction ProgressSync() {\n\t   let variable = JSON.parse(source.getVariable());\n    let status = variable.progress ?? true;\n    if (status) {\n        variable.progress = false;\n        java.toast(\"✘已关闭番茄阅读进度同步\");\n    } else {\n        variable.progress = true;\n        java.toast(\"✔已开启番茄阅读进度同步\");\n    }\n    source.setVariable(JSON.stringify(variable));\n\t}\n\t\nfunction bookSelfSync() {\n\t   let variable = JSON.parse(source.getVariable());\n    let status = variable.bookshelfSync ?? true;\n    if (status) {\n        variable.bookshelfSync = false;\n        java.toast(\"✘已关闭书籍向番茄推送\");\n    } else {\n        variable.bookshelfSync = true;\n        java.toast(\"✔已开启书籍向番茄推送\");\n    }\n    source.setVariable(JSON.stringify(variable));\n\t}\n\t\nfunction bookComment() {\n\t   let variable = JSON.parse(source.getVariable());\n    let status = variable.bookComment ?? true;\n    if (status) {\n        variable.bookComment = false;\n        java.toast(\"✘已关闭阅读段评\");\n    } else {\n        variable.bookComment = true;\n        java.toast(\"✔已开启阅读段评\");\n    }\n    source.setVariable(JSON.stringify(variable));\n\t}\n\t\nfunction author() {\n\t   let url = \"https:\/\/jkapi.com\/api\/one_yan?type=json\";\n\t   let json = java.ajax(url + \",\" + JSON.stringify({\n                   method: \"GET\"\n                }\t)\n\t   \t    );\n\t   \tlet content = JSON.parse(json).content;\n\t   \tjava.log(`\\n${content}`);\n\t   \tjava.longToast(`\\n${content}`);\n\t}\n\nfunction checkAPI() {\njava.toast(\"正在检测更新,时间可能会有点长,请耐心等待!\");\nvar searchPath = action + \"detail.php?bookId=7352711039546297369\";\n\nvar results = [];\nvar fastestApi = null;\nvar minTime = Infinity;\n\nfor (var i = 0; i < api.length; i++) {\n\t   let opins = JSON.stringify({\n\t   \t    method: \"GET\",\n\t   \t    timeout: 5000\n\t   \t});\n    var apiUrl = api[i] + searchPath + \",\" + opins;\n    var startTime = new Date().getTime();\n    \n    try {\n        var response = java.ajax(apiUrl);\n        \n        JSON.parse(response);\n        var endTime = new Date().getTime();\n        var responseTime = endTime - startTime;\n        \n        results.push({\n            api: api[i],\n            time: responseTime,\n            status: \"成功\",\n            success: true\n        });\n        \n        if (responseTime < minTime) {\n            minTime = responseTime;\n            fastestApi = api[i];\n        }\n        \n        \/\/java.log(\"API 测试: \" + apiUrl + \" | 耗时: \" + responseTime + \"ms\");\n        \n    } catch (e) {\n        var endTime = new Date().getTime();\n        var responseTime = endTime - startTime;\n        \n        results.push({\n            api: api[i],\n            time: responseTime,\n            status: \"失败: \" + e.message,\n            success: false\n        });\n        \n        java.log(\"API 测试失败: \" + apiUrl + \" | 错误: \" + e.message);\n    }\n}\nvar report = \"\\n=== API 响应时间测试报告 ===\\n\\n\";\nreport += \"检验书籍: 7352711039546297369\\n\\n\";\n\nfor (var j = 0; j < results.length; j++) {\n    var result = results[j];\n    report += \"API \" + (j + 1) + \": \" + result.api + \"\\n\";\n    report += \"状态: \" + result.status + \"\\n\";\n    report += \"响应时间: \" + result.time + \"ms\\n\";\n    report += \"--------------------------------\\n\";\n}\n\nif (fastestApi) {\n    report += \"\\n✅ 最快 API: \" + fastestApi + \" (\" + minTime + \"ms)\\n\";\n    report += \"推荐使用此接口进行后续请求\\n       已打印至日志\";\n } else {\n    report += \"\\n⚠️ 所有 API 测试均失败,请检查网络连接或 API 状态\";\n }\n   java.log(report);\n   java.longToast(report);\n }\n",
    "respondTime": 180000,
    "ruleBookInfo": {
        "author": "$.author",
        "coverUrl": "<js>\n     replaceCover(java.getString(\"thumb_url\"));\n<\/js>",
        "init": "@js:\nlet data;\nlet mes;\ntry{\n\t   data = JSON.parse(result);\n\t   mes = \"当前来源:\\n\" + java.get(\"mode\");\n\t   } catch(e) {\n\t   \tmes = \"失败!请截图反馈!\";\n\t   \tdata = {content: [{abstract: mes}]}\n\t   \t}\nvar variable = JSON.parse(source.getVariable());\nvar tab = variable.source.tab;\nvar bookId = data[\"data\"][\"book_id\"];\nvar apiIndex = variable.apiIndex;\nvar Cookie = \"sessionid=\" + cookie.getKey(\"fanqienovel.com\", \"sessionid\");\n\nif(variable.bookshelfSync) {\n    let body = {\n    \t    add_book_source: 0,\n    \t    identify_data: [\n    \t        {\n    \t        \t    asterisked: false,\n    \t        \t    book_id: bookId,\n    \t        \t    book_type: 0,\n    \t        \t    modify_time: Date.now()\n    \t        \t}\n    \t    ]\n    \t}\n    \tlet Hander = {\n    \t    \"content-type\": \"application\/json\",\n    \t    \"cookie\": Cookie\n    \t}\n    \tlet prams = [\n    \t    \"aid=1967\",\n    \t    \"iid=0\",\n    \t    \"version_code=57700\",\n    \t    \"update_version_code=57700\"\n    \t].join(\"&\");\n    \tlet opions = JSON.stringify({\n    \t    body: body,\n    \t    charset: \"UTF-8\",\n    \t    headers: Hander,\n    \t    method: \"POST\"\n    \t});\n    \t\/\/java.log(prams);\n    \tlet url = wedapi + \"?\" + prams + \",\" + opions;\n    \ttry{\n    \t\t   java.toast(\"尝试推送至番茄书架……\");\n    \t\t   let response = java.ajax(url);\n    \t\t   let rdata = JSON.parse(response);\n    \t\t   if(rdata[\"code\"] == 0) {\n    \t\t   \t    java.put(\"iinfo\", \"本书已成功推送!\");\n    \t\t   \t} else {\n    \t\t   \t\t   java.put(iinfo, \"错误,同步失败:\" + rdata[\"message\"]);\n    \t\t   \t}\n    \t} catch(e) {\n    \t    java.toast(\"同步失败!\\n\" + e);\n    \t    java.log(e);\n    \t}\n }\n\n\/*\ntry {\n    let update = xGorgon(\n    \t    \"\/reading\/bookapi\/read_history\/update\/v\/?\",\n    \t    \"\",\n    \t    {\n    \t    \t    update_datas: [\n    \t    \t        {\n    \t    \t        \t    book_id: bookId,\n    \t    \t        \t    read_timestamp_ms: Date.now()\n    \t    \t        \t}\n    \t    \t    ]\n    },\n    Cookie\n    );\n    java.log(update)\n} catch (e) {\n    java.toast(\"无法更新阅读历史: \" + e)\n    java.log(\"无法更新阅读历史: \" + e.stack)\n}\n*\/\n\nif(tab == 3) {\n\t   let url = `${api[apiIndex]}${action}studioInfo.php?bookId=${bookId}`;\n\t   let res = JSON.parse(java.ajax(url));\n\t   let title = res.data.map(info =>  info.title);\n\t   java.put(\"ttsInfo\", title);\n\t}\n\n\/\/java.toast(mes);\n\nJSON.stringify(data[\"data\"]||data[\"data\"][0]);",
        "intro": "&nbsp;&nbsp;\n📯 类型:{{JSON.parse(source.variable).source.mode}}{{\"\\n\"+\"​\"}}\n📕 源名:{{$.original_book_name}}\n📖 别名:{{$.book_flight_alias_name}}{{\"\\n\"+\"​\"}}\n🔍 编号:{{$.book_id}}{{\"\\n\"+\"​\"}}\n✏️ 开坑:{{$.create_time##T|\\+.*## }}{{\"\\n\"+\"​\"}}\n🏷️ 分类:{{$.complete_category##\/##,}}\n🏷️ 标签:{{$.tags}}\n👥 主角:{{$.roles##\\[|\\\"|\\]}}\n👁️ 在线:{{$.read_count}}人在读{{\"\\n\"+\"​\"}}\n🔖 状态:__status__{{\"\\n\"+\"​\"}}\n💿 音色支持:{{java.get(\"ttsInfo\")}}{{\"\\n\"+\"​\"}}\n📡  书籍同步状态:{{java.get(\"iinfo\")}}{{\"\\n\"+\"​\"}}\n📜 简介:{{$.abstract}}{{\"\\n\"+\"​\"}}\n📍 {{$.copyright_info##,.*##。}}{{\"\\n\"+\"​\"}}\n@js:\nresult\n.replace(\/.+:(人在读)?\\n\/g, \"\")\n.replace('__status__', (java.getString('$.book_search_visible') == 'true' ? '正常' : (java.getString('$.tomato_book_status') == '3' ? '下架' : '小黑屋')))",
        "kind": "{{$.category}},{{$.score}}分,连载{{$.creation_status}}完结,{{java.timeFormat(java.getString(\"last_chapter_update_time\")*1000)}}\n@js:\nresult\n.replace(\/连载0完结\/g, \"完结\")\n.replace(\/连载1完结\/g, \"连载\")\n.replace(\/连载4完结\/g, \"已断更\")\n.replace(\/连载-1完结\/g, \"完结\")\n.replace(\/\\\/\/g, \"-\")\n.replace(\/\\s..:.*\/g, \"\")",
        "lastChapter": "{{$.last_chapter_title}} • {{java.timeFormat(java.getString(\"last_chapter_update_time\")*1000)}}",
        "name": "$.book_name",
        "tocUrl": "{{api[JSON.parse(source.variable).apiIndex]}}{{action}}directory.php?bookId={{$.book_id}}",
        "wordCount": "$.word_number"
    },
    "ruleContent": {
        "content": "<js>\nconst bookId = book.bookUrl.match(\/bookId\\=([0-9]{19})\/)[1];\nconst itemId = java.hexDecodeToString(result);\nconst variable = JSON.parse(source.getVariable());\nconst { source: { tab }, apiIndex, bookshelfSync, bookComment, lastSyncProgressTime } = variable;\nconst toneId = (variable.studio.toneId || \"tone_id=1\").match(\/tone_id\\=(\\d+)\/)[1];\n\njava.log(\"itemId获取成功:\" + itemId);\n\/\/register(variable.register);\n\nlet url;\nswitch(tab) {\n\t   case 1:\n\t      url = \"content.php?item_id=\" + itemId;\n    break;\n    case 2:\n        url = \"content.php?item_id=\" + itemId;\n    break;\n    case 3:\n        let pram = [\n            `item_id=${itemId}`,\n            `tone_id=${toneId}`\n        ].join(\"&\");\n        url = \"audio.php?\" + pram;\n    break;\n    case 4:\n        let prams = [\n            `book_id=${bookId}`,\n            `item_id=${itemId}`,\n            \"type=html\"\n        ].join(\"&\");\n        url = \"video.php?\" + prams;\n     break;\n}\n\nlet ContentUrl = api[apiIndex] + action + url;\n\n\/\/java.log(ContentUrl);\n\nlet res;\nif(tab != 4 && tab !=1) {\n    res = JSON.parse(java.ajax(ContentUrl));\n}\n\nif(tab ==1 && variable.progress && Date.now() - lastSyncProgressTime > 30000) {\n\t  java.log(\"尝试进行阅读进度同步!\");\n\t  let Cookie = \"sessionid=\" + cookie.getKey(\"fanqienovel.com\", \"sessionid\");\n\t  let body = {\n\t  \t    \"book_id\": bookId,\n\t  \t    \"item_id\": itemId,\n\t  \t    \"read_progress\": 0,\n\t  \t    \"index\": book.durChapterIndex + 1,\n\t  \t    \"read_timestamp\": Date.now(),\n\t  \t    \"genre_type\": 0\n\t  \t}\n\t  \tlet Hander = {\n\t  \t\t   \"user-agent\": \"Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/116.0.0.0 Safari\/537.36\",\n\t  \t\t   \"cookie\": Cookie\n\t  \t}\n\t  \tlet opions = JSON.stringify({\n\t  \t    body: body,\n\t  \t    charset: \"UTF-8\",\n\t  \t    headers: Hander,\n\t  \t    method: \"POST\"\n\t  \t});\n\t  \ttry{\n\t      \tlet bdata = java.ajax(reurl + \",\" + opions);\n\t      \tlet rdata = JSON.parse(bdata);\n\t      \tif(rdata[\"code\"] != 0 || !bdata) {\n\t      \t\t   java.log(\"阅读进度同步失败!\\n\" + rdata[\"message\"]);\n\t      \t\t} else {\n\t      \t\t\t  java.log(rdata[\"message\"]);\n\t      \t\t\t  variable.lastSyncProgressTime = Date.now();\n\t      \t\t\t  source.setVariable(JSON.stringify(variable));\n\t      \t\t}\n\t   } catch(er) {\n\t   \t     java.log(\"阅读进度同步失败!\\n\" + er);\n\t   \t}\n\t}\n\nif(tab == 4) {\n    if(book.durChapterIndex == chapter.index) {\n        java.startBrowser(ContentUrl, chapter.title);\n        java.toast('正在加载视频\\n视频加载较慢,请耐心等待');\n    }\n    result = \"刷新进入短剧播放\";\n} else if(tab == 3) {\n    result = res.data.content.mainUrl;\n} else if(tab == 1) {\n\t   if(bookComment) {\n\t   \t    let cont = java.connect(ContentUrl).body();\n\t   \t    let cresult = JSON.parse(cont).data.content;\n\t   \t    var html = cresult.match(\/<p[^>]*>(.*?)<\\\/p>\/g);\n\t   \t    result = html.map(p => p.replace(\/<p[^>]*>|<\\\/p>\/g, '')).join(\"\\n\");\n\t   \t    result = setend(result, bookId, itemId);\n\t   \t} else {\n\t   \t\t   res = JSON.parse(java.ajax(ContentUrl));\n\t   \t\t   result = getComic(String(res.data.content));\n\t   \t    result.replace(\/\\{\\!\\-\\- PGC_VOICE\\:.*\\-\\-\\}\/g, \"\");\n\t   \t}\n} else if(tab == 2) {\n\t   \/\/java.log(res.data.content);\n\t   result = getComic(String(res.data.content));\n}\n\t   \t    \nresult\n<\/js>",
        "imageStyle": "TEXT",
        "replaceRegex": "@js:\nfunction removeChapterTitleLines(text) {\n    const regex = \/^第\\s*[零一二三四五六七八九十百千万亿\\d]+\\s*章\\s*[::]?\\s*.*$\/gm;\n    let clean = text.replace(regex, '').replace(\/\\n\\s*\\n\/g, '\\n').trim();\n    return clean;\n}\n\nremoveChapterTitleLines(result);\n",
        "title": "$.data.title"
    },
    "ruleExplore": {
        "author": "$.author",
        "bookList": "<js>\n\nlet ck = \"sessionid=\" + (String(cookie.getKey(\"fanqienovel.com\", \"sessionid\")) ? String(cookie.getKey(\"fanqienovel.com\", \"sessionid\")) : (source.getLoginInfoMap() || {})['手动登录Token'])\n\ngetShelf = () => {\nlet book_shelf_url = 'https:\/\/fanqienovel.com\/reading\/bookapi\/bookshelf\/info\/v:version\/?aid=1967&iid=0&version_code=57700&update_version_code=57700'\nlet book_shelf_info = java.ajax(book_shelf_url + ', ' + JSON.stringify({\n  headers: {\n    'Cookie': ck\n  }\n}))\n\nbid = getBookIdFull(book_shelf_info)\nlet id_list = splitArray(bid, 100)\nlet urls = []\nid_list.forEach(i => {\n    urls.push(xGorgon(\"multi-detail\", \"book_id=\" + i.join(\",\"), null, ck))\n})\n\nres = java.ajaxAll(urls)\n\nlet resp = {book_info: []}\nres.forEach(r => {\n  resp.book_info = resp.book_info.concat(JSON.parse(r.body()).data)\n})\n\nreturn resp\n}\n\nfunction getByGroupName(name) {\n  let book_shelf_url = 'https:\/\/fanqienovel.com\/reading\/bookapi\/bookshelf\/info\/v:version\/?aid=1967&iid=0&version_code=57700&update_version_code=57700'\n\n  let book_shelf_info = JSON.parse(java.ajax(book_shelf_url + ', ' + JSON.stringify({\n    headers: {\n      'Cookie': ck,\n    }\n  })))\n  let group_bookids = {\n    \"未分组\": []\n  }\n  \/\/ java.log(\n  book_shelf_info.data.book_shelf_info.forEach(i => {\n    if (!group_bookids[i.group_name ? i.group_name : \"未分组\"]) group_bookids[i.group_name] = []\n    group_bookids[i.group_name ? i.group_name : \"未分组\"].push(i.book_id)\n  })\n  \/\/ java.log(JSON.stringify(group_bookids[\"未分组\"]))\n  \/\/ java.log(decodeURIComponent(name))\n  if (!group_bookids[decodeURIComponent(name)]) return {data: []}\n  \/\/java.log(\"awa\")\n  let book_ids = splitArray(group_bookids[decodeURIComponent(name)], 100)\n  let urls = []\n\n  book_ids.forEach(i => {\n    urls.push(xGorgon(\"multi-detail\", \"book_id=\" + i.join(\",\"), null, ck))\n  })\n  \n  res = java.ajaxAll(urls)\n\n  let resp = {book_info: []}\n  res.forEach(r => {\n    resp.book_info = resp.book_info.concat(JSON.parse(r.body()).data)\n  })\n\n  return resp\n}\n\nfunction getByTabIndex(index) {\n  let url = xGorgon(\n\t  \"bookmall\/tab\",\n    \"version_name=5.8.9.32&device_id=1024&device_type=114514&iid=2048\",\n\t  null,\n\t  ck\n  )\n  let all = JSON.parse(java.ajax(url))\n  let tab = all.data.tab_item[0].cell_data[index]\n  \/\/ java.log(JSON.stringify(tab))\n  tab = tab.cell_data\n  if (!tab) tab = []\n  let bookList = []\n  for (let i of tab) {\n    bookList = bookList.concat(i.book_data)\n    \/\/ java.log(JSON.stringify(i.book_data))\n  }\n  return { book_info: bookList }\n}\n\nif (baseUrl.endsWith(\"bookshelf\")) result = getShelf(\"bookshelf\/info\")\nelse {\n  let w = baseUrl.split(\"\/\")\n  if (baseUrl.includes(\"groupName\")) {\n    result = getByGroupName(w[w.length - 1])\n  } else if (baseUrl.includes(\"tab\")) {\n    result = getByTabIndex(parseInt(w[w.length - 1]))\n  } else result = JSON.parse(result)\n}\nJSON.stringify({data: result.book_info || result.data.book_info || result.detail_list || result.data})\n<\/js>\n$.data[*]",
        "bookUrl": "@js:\nlet variable = JSON.parse(source.getVariable());\nlet apiIndex = variable.apiIndex;\n\napi[apiIndex] + action + \"detail.php?bookId={{$.book_id||$.series_id}}\";",
        "coverUrl": "$.thumb_url",
        "intro": "$.abstract##\\n",
        "kind": "{{$.category}}\n{{$.status}}\n{{$.source}}\n{{$.tags}}\n男生{{$.gender}}女生\n连载{{$.creation_status}}完结\n{{$.score}}分\n{{$..text}}\n{{$.sub_info}}\n##连载0|1完结|男生0|1女生|男生女生[\\s\\S]*\n@js:result\n.replace(\"男生2女生\", \"出版\")\n.replace(\"连载4完结\", \"断更\")\n.replace(\"连载-1完结\", \"未知\");",
        "lastChapter": "{{$.last_chapter_title}} • {{$.last_update_time}}",
        "name": "$.book_name",
        "wordCount": "$.word_number"
    },
    "ruleSearch": {
        "author": "$.author||$..copyright",
        "bookList": "<js>\nlet list = [];\nlet book_list = [];\nlet res = JSON.parse(result);\n\ntry {\n  if (res.data.search_tabs) {\n    for (let i = 0; i < res.data.search_tabs.length; i++) {\n        let books = res[\"data\"][\"search_tabs\"][i][\"data\"];\n        if (books != null) list = list.concat(books);\n    }\n  }\n} catch (e) {\n\t\n}\n\nfor (let $ of list) {\n   book_list.push($.book_data ? $.book_data[0] : ($.video_data ? $.video_data[0] : $));\n}\n\nJSON.stringify(book_list)\n<\/js>\n$[*]",
        "bookUrl": "{{api[JSON.parse(source.variable).apiIndex]}}{{action}}detail.php?bookId={{$.book_id||$.series_id}}",
        "checkKeyWord": "m:校花",
        "coverUrl": "@js:replaceCover(java.getString(\"thumb_url||cover\"))",
        "intro": "$.abstract||$..series_intro##\\n",
        "kind": "男生{{$.gender}}女生\n连载{{$.creation_status}}完结\n{{$.score}}分\n{{$..text}}\n{{$.sub_info}}\n##连载0|1完结|男生0|1女生|男生女生[\\s\\S]*\n@js:result\n.replace(\"男生2女生\",\"出版\")\n.replace(\"连载4完结\",\"断更\")\n.replace(\"连载-1完结\",\"未知\");",
        "lastChapter": "$.sub_title",
        "name": "$.book_name||$..series_title##(别名:.*?)",
        "wordCount": "$.word_number"
    },
    "ruleToc": {
        "chapterList": "@js:\nconst { source: { tab } } = JSON.parse(source.variable);\nbook.type = { 2: 64, 3: 32 }[tab] || 8;\n\nvar u_Index = 0;     \/\/ 0-1\n\/\/ java.log(book)\nvar u_Types = ['API', 'WEB'];\nvar v_Index = [];\nvar v_Names = [];\nvar c_Array = [];\nvar book_id = book.bookUrl.match(\/bookId\\=([0-9]{19})\/)[1];\n\nfunction b64Url(item_id) {\n\t   let type = JSON.stringify({\n\t   \t    type: \"星眠\"\n\t   \t});\n\t   \tlet itemId = java.base64Encode(item_id);\n    return `data:content;base64,${itemId},${type}`;\n}\n\nfunction push_a(a, b, c, d) {\n    c_Array.push({\n        \"ChapterName\": a || \"\",\n        \"isVolume\": b || false,\n        \"ChapterUrl\": c ? b64Url(c) : \"\",\n        \"ChapterInfo\": (d || \"\")\/\/.replace(\/第.卷:默认 \\|\/g, \"\").trim(),\n    });\n}\n\nfunction push_b(list) {\n    for (let c of list) {\n        let c_name = c.volume_name || \"第一卷:默认\";\n        let c_time = java.timeFormatUTC(c.first_pass_time * 1000, 'yyyy-MM-dd HH:mm:ss', 288000000);\n        let c_word = c.chapter_word_number + \"字\";\n        let c_info = [c_name, c_time, c_word];\n        let v_name = v_Names[v_Names.length - 1];\n        if (v_name != c_name) {\n        \t   \/\/ java.log(c_name);\n            v_Index.push(c_Array.length);\n            v_Names.push(c_name);\n            push_a(c_name, true);\n        }\n        push_a(c.title, false, c.item_id, c_info.join(\" | \"));\n    }\n}\n\n\/\/  app接口  ##卷名&时间&字数\nswitch (String(u_Types[u_Index])) {\n    case 'API':\n        let data = JSON.parse(java.ajax(xGorgon(\n            \"directory\/all_items\",\n            \"book_id=\" + book_id\n        )));\n\n        if (data.code == 0) {\n            push_b(data.data.item_data_list);\n            break;\n        }\n        \/\/ java.log(\"下架书籍,使用web接口获取目录\");\n\n\n\n        \/\/  web接口  ##卷名&时间\n    case 'WEB':\n        let Json, Host = \"https:\/\/fanqienovel.com\";\n        if (false) {\n            Json = java.ajax(Host + \"\/page\/\" + book_id)\n                .match(\/__INITIAL_STATE__=([^;]+)\/)[1];\n        } else {\n            Json = java.ajax(Host + \"\/api\/reader\/directory\/detail?bookId=\" + book_id);\n        }\n        \/\/ java.log(Json)\n        let List = JSON.parse(Json).data.chapterListWithVolume;\n        if (List) {\n          for (let list of List) {\n              v_Index.push(c_Array.length);\n              list.map((_, i) => {\n                  let c_name = _.volume_name || \"第一卷:默认\";\n                  let c_time = java.timeFormat(_.firstPassTime * 1000);\n                  if (!i) push_a(c_name, true);\n                  push_a(_.title, false, _.itemId, [c_name, c_time].join(\" | \"))\n              });\n          }\n          break;\n        }\n}\n\n\n\n\/\/ 返回数组\nlet len = v_Index.length;\n\/\/ java.log(JSON.stringify(v_Index))\n\/*\nif (len < 2) { \/\/ 分卷太少不显示\n    for (; len > 0; len--) {\n        c_Array.splice(v_Index[len - 1], 1);\n    }\n} else \n*\/\nif (false) { \/\/ 显示效果不好看\n    for (let a, b, i = 0; i < len; i++) {\n        a = v_Index[i];\n        b = v_Index[i + 1];\n        if (b == undefined) b = len;\n        c_Array[a][\"ChapterInfo\"] = \"共 \" + (b - a - 1) + \" 章\";\n    }\n}\n\nc_Array",
        "chapterName": "ChapterName\n@js:\nfunction cleanTitleAdvanced(title) {\n  const parts = title.split(\/\\s*\/);\n  const uniqueParts = [];\n  const seen = new Set();\n  \n  for (const part of parts) {\n    if (\/^第\\d+章$\/.test(part) && seen.has(part)) {\n      continue;\n    }\n    uniqueParts.push(part);\n    seen.add(part);\n  }\n  \n  return uniqueParts.join(\" \");\n}\n\ncleanTitleAdvanced(result)",
        "chapterUrl": "ChapterUrl",
        "isVolume": "isVolume",
        "updateTime": "ChapterInfo##\/##-"
    },
    "searchUrl": "<js>\nlet variable = JSON.parse(source.getVariable());\nconst { source: { tab, mode: show }, apiIndex } = variable;\n\nvar again = false;\nvar keyStr = String(key);\n\nvar prefixMap = {\n    \"m:\": { tab: 2, show: \"🌄漫画\" },\n    \"t:\": { tab: 3, show: \"💿听书\" },\n    \"d:\": { tab: 4, show: \"📹短剧\" }\n};\n\nvar prefixes = [\"m:\", \"t:\", \"d:\"];\nfor (var i = 0; i < prefixes.length; i++) {\n    var prefix = prefixes[i];\n    if (keyStr.indexOf(prefix) === 0) {\n        var config = prefixMap[prefix];\n        tab = config.tab;\n        show = config.show;\n        key = keyStr.substring(2);\n        variable.source.tab = config.tab;\n        variable.source.mode = config.show;\n        again = true;\n        break;\n    }\n}\n\nif (again) {\n    source.setVariable(JSON.stringify(variable));\n}\n\nlet prams = [\n    `query=${key}`,\n    `offset=${( page - 1) * 10}`,\n    `tab=${tab}`\n]\n\nfunction Search(key, apiIndex, prams) {\n\t    let is_id = \/^\\d+$\/.test(key);\n\t    if(!is_id) {\n          let search = api[apiIndex] + action + \"search.php?\" + prams.join(\"&\");\n          return search\n   } else {\n        \t  let book_url = api[apiIndex] + action + \"detail.php?bookId=\" + key;\n   \t       return book_url\n   \t}\n }\n \njava.toast(\"\\n当前来源:\\n\" + show);\njava.put(\"mode\", show);\nSearch(key, apiIndex, prams);\n<\/js>",
    "weight": 0
}
广告