✨ 阅文集团聚合
https://yuewen.com
情无羁 (8652)11/04 10:48
起点读书webs限免共享
该书源本体来自Yiove仓库,修改正文为服务器缓存内容,不可自行解锁
{
"bookSourceComment": "",
"bookSourceGroup": "",
"bookSourceName": "✨ 阅文集团聚合",
"bookSourceType": 0,
"bookSourceUrl": "https:\/\/yuewen.com",
"customOrder": 200,
"enabled": true,
"enabledCookieJar": true,
"enabledExplore": false,
"exploreUrl": "",
"header": "",
"jsLib": "const encryptKeys = {\n headers: {\n QDSign: {\n key: \"{1dYgqE)h9,R)hKqEcv4]k[h\",\n iv: \"01234567\"\n },\n QDInfo: {\n key: \"0821CAAD409B84020821CAAD\",\n iv: String.fromCharCode(0, 0, 0, 0, 0, 0, 0, 0)\n }\n }\n}\n\nfunction getDeviceId(java, source) {\n if (!java || !source) {\n java = this.java\n source = this.source\n }\n let sVar = JSON.parse(!Packages.android.text.TextUtils.isEmpty(source.getVariable()) ? source.getVariable() : \"{}\")\n if (!sVar.deviceid) {\n let temp = \"\"\n let chr = \"0123456789abcdef\".split(\"\")\n for (let i = 0; i < 16; i++) { temp = temp + chr[Math.floor(Math.random() * chr.length)]}\n java.log(temp)\n sVar.deviceid = temp\n source.setVariable(JSON.stringify(sVar, null, 2))\n return temp\n }\n return sVar.deviceid\n}\n\nfunction QDSign(path, params, page, dmode) {\n \/*\n 为保护作者人身氨醛,本函数不包含付费正文的核心请求头检查,只有基础过检测\n \t 基本够正常使用,如果你有实力可以把其他头(gorgon一类的)自己补全\n *\/\n const { java, source } = this\n let a = params.sort().join(\"&\").replace(\/\\$page\/gi, page)\n let t = Date.now()\n\n \/\/ Sign参数\n let sign = \"Rv1rPTnczce|\" + t + \"||||||\" + java.md5Encode(a.toLowerCase())\n let Sign = java.tripleDESEncodeBase64Str(sign, encryptKeys.headers.QDSign.key, \"CBC\", \"PKCS5Padding\", encryptKeys.headers.QDSign.iv)\n\n \/\/ Info参数\n let id = getDeviceId(java, source)\n let info = id + \"||||||1||999|\" + t\n let Info = java.tripleDESEncodeBase64Str(info, encryptKeys.headers.QDInfo.key, \"CBC\", \"PKCS5Padding\", encryptKeys.headers.QDInfo.iv)\n\n if (dmode) {\n return {\n url: \"https:\/\/druidv6.if.qidian.com\/argus\/api\/\" + path + \"?\" + a,\n headers: {\n QDSign: Sign,\n QDInfo: Info,\n tstamp: String(t)\n }\n }\n }\n \/\/ Url拼接\n return \"https:\/\/druidv6.if.qidian.com\/argus\/api\/\" + path + \"?\" + a + \",\" + JSON.stringify({\n headers: {\n QDSign: Sign,\n QDInfo: Info,\n tstamp: String(t)\n }\n })\n}\n\nvar ji = new JavaImporter()\nji.importPackage(Packages.okhttp3)\nwith(ji) {\n function getBytes(url, headers) {\n let client = new OkHttpClient()\n\n let requestBuilder = new Request.Builder()\n .url(url)\n .get()\n\n \/\/ 添加请求头\n for (let headerName in headers) {\n requestBuilder.addHeader(headerName, headers[headerName])\n }\n\n let request = requestBuilder.build()\n let response = client.newCall(request).execute()\n\n try {\n \/\/ 处理响应\n let responseBody = response.body().bytes()\n return responseBody\n } finally {\n response.close()\n }\n }\n}\n\nfunction dContentQD(cid, body) {\n const { java, source } = this\n let aid = getDeviceId(java, source)\n let sha1 = \"0\" + aid + cid + \"2EEE1433A152E84B3756301D8FA3E69A\"\n let key0 = java.HMacBase64(sha1, \"HMAC-SHA1\", aid).slice(0, -4)\n let key1 = java.base64Decode(\"BjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2Ng==\")\n let key2 = java.md5Encode(key1 + key0 + aid)\n let key3 = java.hexDecodeToByteArray(\"6c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c\" + key2)\n\n let md5 = Packages.cn.hutool.crypto.digest.DigestUtil.md5(key3)\n let B = Packages.android.util.Base64\n let key = B.encodeToString(md5, B.NO_WRAP)\n let iv = java.base64Decode(\"AAAAAAAAAAA=\")\n\n return java.createSymmetricCrypto(\"DESede\/CBC\/PKCS5Padding\", key0, iv).decryptStr(java.createSymmetricCrypto(\"DESede\/CBC\/PKCS5Padding\", key, iv).decrypt(Array.from(body).slice(8, -16)))\n}\n\nfunction getLoginHeader() {\n const { source } = this\n return JSON.parse(!Packages.android.text.TextUtils.isEmpty(source.getLoginHeader()) ? source.getLoginHeader().slice(0, -1) : \"{}\")\n}\n\nfunction putLoginHeader(data) {\n const { source } = this\n source.putLoginHeader(\n JSON.stringify(data) + \"_\" \/\/ prevent default: 防止阅读将登录头数据加到请求头里\n )\n}",
"lastUpdateTime": "1762224496004",
"loginUi": "[\n{\n type: \"button\",\n name: \" 起点相关 \",\n action: \"empty()\"\n},\n{\n type: \"button\",\n\tname: \" [浏览器登录] \",\n\taction: \"proxyExcWrapper(browserLogin)\"\n},\n{\n type: \"button\",\n\tname: \" [凭据登录] \",\n\taction: \"proxyExcWrapper(tokenLogin)\"\n},\n{\n type: \"text\",\n name: \"ywguid\"\n},\n{\n type: \"password\",\n name: \"ywkey\"\n}\n]",
"loginUrl": "<js>\nfunction browserLogin() {\n try {\n java.startBrowserAwait(\n \"https:\/\/qidian.com\", \n \"登录\"\n )\n } catch (e) {}\n let ywkey = cookie.getKey(\"qidian.com\", \"ywkey\")\n let ywguid = cookie.getKey(\"qidian.com\", \"ywguid\")\n putLoginHeader({\n \t guid: ywguid,\n \t key: ywkey\n })\n cookie.removeCookie(\"qidian.com\")\n \/\/ java.toast(\"没做完:(\")\n}\n\nfunction login() {}\n\nfunction tokenLogin() {\n if (source.getLoginInfoMap() == null) {\n java.toast(\"未获取到LoginInfoMap,请尝试重新打开登录UI再按一遍\")\n }\n putLoginHeader({\n guid: source.getLoginInfoMap()[\"ywguid\"],\n key: source.getLoginInfoMap()[\"ywkey\"]\n })\n java.toast(\"凭据保存成功\")\n}\n\nfunction proxyExcWrapper(fun) {\n try {\n fun()\n } catch (e) {\n java.log(e.toString() + \"\\n\" + e.stack)\n }\n}\n\nfunction empty() {\n java.toast(\"别点了,用来看的\")\n}\n<\/js>",
"respondTime": 1436,
"ruleBookInfo": {
"author": "Author",
"coverUrl": "https:\/\/bookcover.yuewen.com\/qdbimg\/349573\/{{$.BookId}}\/600.webp",
"init": "<js>\nvar Regs = [\/bookId=(\\d+)\/i, \/\\.com\\D+?\\\/(\\d+)\/i, \/(\\d+)\/i];\nvar bookId = \"\";\nfor (let Reg of Regs) {\n let mat = baseUrl.match(Reg);\n if (mat) {\n bookId = mat[1];\n break;\n }\n}\njava.ajax(QDSign(\"v2\/bookdetail\/get\", [\n \"bookId=\" + bookId\n]))\n<\/js>\n$.Data",
"intro": "<p>{{$.Description}}",
"kind": "{{java.getString(\"$..IsInBlackList\")==1?\"非限免\":\"限免\"}}|{{$.BookStatus##完本##完结}}|{{String(java.timeFormat(java.getString(\"LastVipChapterUpdateTime\")==0?java.getString(\"LastChapterUpdateTime\"):java.getString(\"LastVipChapterUpdateTime\"))).replace(\/\\s*\\d+:\\d+\/,\"\")}}\n{{(java.getString(\"$.WordsCnt\")\/10000).toFixed(1)+\"万\"}}\n{{$.CategoryName}}|{{$.SubCategoryName}}\n<js>##(?m)\\|$<\/js>",
"lastChapter": "{{$.LastVipUpdateChapterName||$.LastUpdateChapterName}}",
"name": "BookName",
"tocUrl": "BookId\n<js>java.base64Encode(result)<\/js>\ndata:bookId;base64,{{result}},{\"type\":\"ywc\"}",
"wordCount": ""
},
"ruleContent": {
"content": "<js>\nlet info = JSON.parse(java.hexDecodeToString(result))\n\nlet content = \"\"\nlet header = getLoginHeader()\n\nswitch (info.source) {\n case \"qd\":\n let bid = java.hexDecodeToString(java.ajax(book.tocUrl))\n let cid = info.cid\n if (info.isVip) {\n let url = `https:\/\/static.yesui.me\/api\/content.php?bookId=${bid}&chapterId=${cid}`\n java.log(url)\n result=java.ajax(url)\n java.log(result)\n content=JSON.parse(result).content\n \n } else {\n let request = QDSign(\"v2\/bookcontent\/safegetcontent\", [\n \"bookId=\" + bid,\n \"chapterId=\" + cid\n ], \"\", true)\n\n let body = getBytes(request.url, request.headers)\n let decrypt = dContentQD(cid, body)\n let raw = JSON.parse(decrypt)\n let cArr = raw.Content.split(\"\\r\\n\")\n let blocks = {}\n for (let i of raw.Blocks) {\n blocks[i.Sort] = i\n }\n for (let i = 0; i < cArr.length; i++) {\n content += cArr[i] + \"\\n\"\n if (blocks[i + 1] && blocks[i + 1].Type == \"image\") content += `<img src=\"${blocks[i + 1].Url}\">\\n`\n }\n }\n}\n\ncontent\n<\/js>",
"imageStyle": "FULL",
"replaceRegex": ""
},
"ruleExplore": {
"author": "",
"bookList": "",
"bookUrl": "",
"coverUrl": "",
"intro": "",
"kind": "",
"name": "",
"wordCount": ""
},
"ruleSearch": {
"author": "AuthorName||Author",
"bookList": ".BookCard&&Data.Books&&Data.Data.Items||Data.RankBookList",
"bookUrl": "https:\/\/druid.if.qidian.com\/argus\/api\/v2\/bookdetail\/get?bookId={{$.BookId}}",
"checkKeyWord": "",
"coverUrl": "https:\/\/bookcover.yuewen.com\/qdbimg\/349573\/{{$.BookId}}\/600.webp",
"intro": "Description||Desc",
"kind": "{{$.ActionStatusString||$.BookStatus##完本##完结}}\n{{(java.getString(\"$.WordsCount\")\/10000).toFixed(1)+\"万\"}}\n{{$.CategoryName}}|{{$.SubCategoryName}}\n<js>##(?m)\\|$<\/js>",
"lastChapter": "",
"name": "BookName",
"wordCount": ""
},
"ruleToc": {
"chapterList": "@js:\nbookId = java.hexDecodeToString(result);\n$ = JSON.parse(java.ajax(QDSign(\"v1\/chapterlist\/chapterlist\", [\n \"bookId=\" + bookId\n])))[\"Data\"];\nv_Names = {};\nc_Array = [];\nc_Json = {};\n\n\nfunction b64Url(item_id) {\n return `data:chapterId;base64,${java.base64Encode(item_id)},{\"type\":\"ywc\"}`;\n}\n\n\nfunction push(vc, a, b, c, d, e, f) {\n let list = c_Json[vc];\n if(!list) c_Json[vc] = list = [];\n \/\/ if (!e==1 || false) return\n list.push({\n \"ChapterName\": a || \"\",\n \"isVolume\": b || false,\n \"chapterUrl\": c ? b64Url(\n JSON.stringify({\n source: \"qd\",\n cid: c || null,\n isVip: e==1 || false,\n isPay: f==1 || false,\n ccid: null,\n cbid: null\n })) : \"\",\n \"ChapterInfo\": d || \"\",\n \"isVip\": e==1 || false,\n \"isPay\": f==1 || false\n });\n}\n\n\nlet t_List = $.Volumes;\nfor (let $$ of t_List) {\n\t let Vc = $$.VolumeCode;\n v_Names[Vc] = $$.VolumeName;\n c_Json[Vc] = [];\n if (t_List.length > 1) {\n push(Vc, $$.VolumeName, true);\n }\n}\n\n\nt_List = $.Chapters.slice(1);\nfor (let $$ of t_List) {\n let c_name = v_Names[$$.Vc] || \"未知分卷\";\n let c_time = java.timeFormatUTC($$.T, 'yyyy-MM-dd HH:mm:ss', 12);\n let c_word = $$.W + \"字\";\n let c_info = [c_name, c_time, c_word].join(\"丨\");\n push($$.Vc, $$.N, false, $$.C, c_info, $$.V, $$.P);\n}\n\n\nfor(let i in c_Json){\nc_Array = c_Array.concat(c_Json[i]);\n}\nc_Array;",
"chapterName": "ChapterName",
"chapterUrl": "chapterUrl",
"isPay": "isPay",
"isVip": "isVip",
"isVolume": "isVolume",
"preUpdateJs": "",
"updateTime": "ChapterInfo"
},
"searchUrl": "@js:\nQDSign(\"v1\/booksearch\/searchbooks\", [\n \"keyword=\" + java.key,\n \"pageIndex=\" + page,\n \"pageSize=20\"\n])",
"weight": 0
}