💫悦恋免费小说 - [☆星眠☆]
情不知所起一往而深, 情不知所终一笑而泯。 ––––––––––––––––@星眠
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}¶_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": " \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
}