🌞晴天融合4.11.9(vip完全版)
晴天融合VIP4.0
晴天 (8653)3天前
全网最全聚合书源,支持30+主流平台
{
"bookSourceComment": "更新日志请点击登录,更新书源中查看\n\n兼容正式版阅读app",
"bookSourceGroup": "晴天聚合",
"bookSourceName": "🌞晴天融合4.11.9(vip完全版)",
"bookSourceType": 0,
"bookSourceUrl": "晴天融合VIP4.0",
"bookUrlPattern": "https?:\\\/\\\/(?:[a-zA-Z0-9]+\\.)*(?:qingtian618\\.com|gyks\\.cf)\\\/detail.*",
"customButton": false,
"customOrder": -1,
"enabled": true,
"enabledCookieJar": false,
"enabledExplore": true,
"eventListener": false,
"exploreUrl": "<js>\nfunction setArgument(key, value) {\n var open_argument = source.getVariable();\n open_argument = getArguments(open_argument, '') || {};\n open_argument[key] = value;\n var result = JSON.stringify(open_argument);\n source.setVariable(result);\n return result;\n}\n\nvar open_argument = source.getVariable();\nvar base_url = getArguments(open_argument, 'server') || '';\nvar ms = getArguments(open_argument, 'tone_id') || '';\nvar source_type = getArguments(open_argument, 'source_type') || '男频';\nvar tab = getArguments(open_argument, 'media') || '小说';\nvar sources = getArguments(open_argument, 'source') || '番茄';\n\n\nvar sdtoken;\ntry {\n var loginInfoMap = source.getLoginInfoMap ? source.getLoginInfoMap() : {};\n sdtoken = String(loginInfoMap['手动填写番茄token(可不填)'] || '');\n} catch (e) {\n sdtoken = '';\n}\n\nvar rawCookie = getFanqieCookie() || sdtoken;\nvar match = rawCookie.match(\/sessionid=[^;]+\/);\nvar fqcookie = match ? match[0] : '';\nvar fqssionid = '';\nif (!fqcookie) {\n java.toast('您还未登陆番茄账号,无法同步数据哦!');\n} else {\n fqssionid = getSessionId(fqcookie)\n}\nvar fqsjurl = base_url + \"\/bookshelf?page={{page}}&ssionid=\" + fqssionid;\nvar fqtjurl = base_url + \"\/fqrecommend?page={{page}}&ssionid=\" + fqssionid;\nvar fqlsurl = base_url + \"\/fqhistory?page={{page}}&ssionid=\" + fqssionid;\n\nvar groupDatas = [];\nvar infoData = [];\n\nfunction deviceType() {\n try {\n return !!java.androidId();\n } catch (e) {\n return false;\n }\n}\n\nvar hasValidCookie = fqcookie.length > 0;\n\nif (hasValidCookie) {\n function groupQuery() {\n try {\n var url = base_url + \"\/group_name?ssionid=\" + fqssionid;\n var res = java.ajax(url);\n var response = JSON.parse(res);\n\n if (!(response && response.data)) {}\n\n response.data.forEach(function(group) {\n var keys = Object.keys(group);\n if (keys.length > 0) {\n var key = keys[0];\n var value = group[key];\n if (value && value.length) {\n var option = {\n \"method\": \"POST\",\n \"body\": {\n \"book_ids\": value,\n \"page\": \"{{page}}\"\n }\n };\n groupDatas.push({\n title: key,\n url: base_url + \"\/bookshelf,\" + JSON.stringify(option),\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 0.45\n }\n });\n }\n }\n });\n\n if (groupDatas.length % 2 !== 0) {\n groupDatas.push({\n title: \"--\",\n url: \"\",\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 0.45\n }\n });\n }\n } catch (e) {\n java.longToast(\"番茄登录过期,已隐藏番茄书架\");\n }\n }\n\n try {\n java.longToast(\"正在加载分组数据...\");\n var userUrl = base_url + \"\/fquser?ssionid=\" + fqssionid;\n var userRes = java.ajax(userUrl);\n var userData = JSON.parse(userRes);\n\n var userName = (userData && userData.data && userData.data.name) ? userData.data.name : '未知用户';\n if (!userName.includes('未知用户')) {\n infoData = [{\n title: userName + \"个人中心\",\n url: fqsjurl,\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 1\n }\n }, {\n title: \"个性推荐(番茄)\",\n url: fqtjurl,\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 0.45\n }\n }, {\n title: \"历史阅读(番茄)\",\n url: fqlsurl,\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 0.45\n }\n }];\n }\n groupQuery();\n } catch (e) {\n java.longToast(\"番茄登录过期,已隐藏番茄书架\");\n }\n}\n\nvar style_list = [];\ntry {\n var durl = `${base_url}\/discovestyle?source=${sources}&source_type=${source_type}&tab=${tab}`;\n var res = java.ajax(durl);\n var result = JSON.parse(res);\n style_list = result.data || [];\n if (result.msg) {\n java.toast(result.msg);\n }\n} catch (e) {\n java.toast(\"发现样式获取失败\");\n}\n\nvar finalData = infoData.concat(groupDatas, style_list);\nJSON.stringify(finalData);\n<\/js>",
"header": "{ \"User-Agent\":\"Mozilla\/5.0 (Linux; Android 6.0; Nexus 5 Build\/MRA58N) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/138.0.0.0 Mobile Safari\/537.36 Edg\/138.0.0.0\" }",
"jsLib": "var host = [\n 'https:\/\/v1.gyks.cf',\n 'https:\/\/v2.gyks.cf',\n 'https:\/\/v3.gyks.cf',\n 'https:\/\/v4.gyks.cf',\n 'https:\/\/v5.gyks.cf',\n 'https:\/\/v6.gyks.cf',\n 'https:\/\/v7.gyks.cf',\n 'http:\/\/101.35.133.34:8888',\n 'http:\/\/103.236.91.147:8888'\n];\n\nfunction getArguments(open_argument, key) {\n try {\n open_argument = JSON.parse(open_argument);\n } catch (e) {\n open_argument = {\n media: \"小说\",\n tone_id: \"默认音色\",\n server: host[0],\n source: \"全部\",\n source_type: \"男频\",\n };\n }\n if (key) {\n return open_argument[key];\n } else {\n return open_argument;\n }\n}\n\nfunction setArguments(key, value) {\n const {\n source\n } = this;\n let open_argument = source.getVariable();\n open_argument = getArguments(open_argument, '');\n open_argument[key] = value;\n open_argument = JSON.stringify(open_argument);\n source.setVariable(open_argument);\n return open_argument;\n}\n\nfunction decrypt(Text) {\n return Text;\n}\n\nfunction paraForAndroid(content, sources) {\n let {\n java,\n cache,\n source\n } = this;\n let plcolor = getArguments(source.getVariable(), \"plcolor\");\n if (!plcolor) {\n plcolor = \"#000000\";\n }\n\n const createSvg = this.createSvg.bind(this);\n\n return content.replace(\/<p>(.*?)(?:<comment ident=\"([^\"]*)\" count=\"([^\"]*)\" \\\/>)?<\\\/p>\/g,\n (match, text, url, count) => {\n if (url && count) {\n const click = 0;\n cache.putMemory(url, click);\n const encodedUrl = url;\n return `<p>${text}<img src=\"${createSvg(count, plcolor,encodedUrl,sources)}\"><\/p>`;\n } else {\n return `<p>${text}<\/p>`;\n }\n }\n );\n}\n\n\nfunction showCmt(url, sources) {\n let {\n java,\n cache\n } = this;\n\n const currentTime = Date.now();\n const click = cache.getFromMemory(url);\n let isqread = false;\n try {\n java.qread();\n isqread = true;\n } catch (e) {}\n if (click < 1 && !isqread) {\n cache.putMemory(url, click + 1);\n return;\n } else {\n try {\n java.startBrowserDp(url, sources + '段评');\n } catch (e) {\n java.startBrowser(url, sources + '段评');\n }\n }\n}\n\n\n\nfunction createSvg(number, color, encodedUrl, sources) {\n var displayText = number > 99 ? \"99+\" : number.toString();\n var loginInfoMap = {};\n\n if (this.source && typeof this.source.getLoginInfoMap == 'function') {\n loginInfoMap = this.source.getLoginInfoMap() || {};\n }\n\n var bubbleStyle = String(loginInfoMap['段评气泡样式'] || '0');\n var svg;\n\n \/\/ 样式1:精致圆形 - 简洁优雅\n if (bubbleStyle == '1') {\n svg = '<svg width=\"1000\" height=\"1000\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">' +\n '<circle cx=\"500\" cy=\"500\" r=\"440\" fill=\"none\" stroke=\"' + color + '\" stroke-width=\"32\" opacity=\"0.2\"\/>' +\n '<circle cx=\"500\" cy=\"500\" r=\"440\" fill=\"none\" stroke=\"' + color + '\" stroke-width=\"4\"\/>' +\n '<text x=\"500\" y=\"500\" font-family=\"-apple-system, sans-serif\" text-anchor=\"middle\" ' +\n 'font-size=\"400\" fill=\"' + color + '\" dy=\"0.35em\" font-weight=\"500\">' + displayText + '<\/text>' +\n '<\/svg>';\n }\n \/\/ 样式2:微信风格 - 熟悉亲切的对话气泡\n else if (bubbleStyle == '2') {\n svg = '<svg width=\"1000\" height=\"900\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">' +\n '<path d=\"M120,120 L880,120 Q920,120 920,160 L920,680 Q920,720 880,720 L280,720 L120,840 L120,720 Q80,720 80,680 L80,160 Q80,120 120,120 Z\" ' +\n 'fill=\"none\" stroke=\"' + color + '\" stroke-width=\"24\" stroke-linejoin=\"round\"\/>' +\n '<text x=\"500\" y=\"440\" font-family=\"-apple-system, sans-serif\" text-anchor=\"middle\" ' +\n 'font-size=\"360\" fill=\"' + color + '\" dy=\"0.32em\" font-weight=\"500\">' + displayText + '<\/text>' +\n '<\/svg>';\n }\n \/\/ 样式3:悬浮标签 - 扁平现代风格\n else if (bubbleStyle == '3') {\n svg = '<svg width=\"1000\" height=\"600\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">' +\n '<rect x=\"80\" y=\"80\" width=\"840\" height=\"440\" rx=\"220\" ry=\"220\" ' +\n 'fill=\"none\" stroke=\"' + color + '\" stroke-width=\"6\"\/>' +\n '<rect x=\"100\" y=\"100\" width=\"800\" height=\"400\" rx=\"200\" ry=\"200\" ' +\n 'fill=\"none\" stroke=\"' + color + '\" stroke-width=\"20\" opacity=\"0.3\"\/>' +\n '<text x=\"500\" y=\"310\" font-family=\"-apple-system, sans-serif\" text-anchor=\"middle\" ' +\n 'font-size=\"280\" fill=\"' + color + '\" dy=\"0.32em\" font-weight=\"500\" letter-spacing=\"8\">' + displayText + '<\/text>' +\n '<\/svg>';\n }\n \/\/ 样式4:书签标注 - 符合阅读场景\n else if (bubbleStyle == '4') {\n svg = '<svg width=\"900\" height=\"1000\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">' +\n '<path d=\"M150,80 L750,80 Q800,80 800,130 L800,870 Q800,920 750,920 L480,920 L450,980 L420,920 L150,920 Q100,920 100,870 L100,130 Q100,80 150,80 Z\" ' +\n 'fill=\"none\" stroke=\"' + color + '\" stroke-width=\"24\" stroke-linejoin=\"round\"\/>' +\n '<line x1=\"150\" y1=\"280\" x2=\"750\" y2=\"280\" stroke=\"' + color + '\" stroke-width=\"3\" opacity=\"0.3\"\/>' +\n '<text x=\"450\" y=\"520\" font-family=\"Georgia, serif\" text-anchor=\"middle\" ' +\n 'font-size=\"360\" fill=\"' + color + '\" dy=\"0.32em\" font-weight=\"400\">' + displayText + '<\/text>' +\n '<text x=\"450\" y=\"800\" font-family=\"-apple-system, sans-serif\" text-anchor=\"middle\" ' +\n 'font-size=\"70\" fill=\"' + color + '\" opacity=\"0.6\" letter-spacing=\"3\">评论<\/text>' +\n '<\/svg>';\n }\n \/\/ 默认返回样式0\n else {\n svg = '<svg width=\"160\" height=\"120\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">' +\n '<path d=\"M 55 10 ' +\n 'L 120 10 ' +\n 'Q 150 10 150 40 ' +\n 'L 150 80 ' +\n 'Q 150 110 120 110 ' +\n 'L 55 110 ' +\n 'Q 25 110 25 80 ' +\n 'L 25 75 ' +\n 'L 3 60 ' +\n 'L 25 45 ' +\n 'L 25 40 ' +\n 'Q 25 10 55 10 Z\" ' +\n 'fill=\"none\" ' +\n 'stroke=\"' + color + '\" ' +\n 'stroke-width=\"2\" ' +\n 'stroke-linejoin=\"round\"\/>' +\n '<!-- 数字文本 -->' +\n '<text x=\"87\" y=\"75\" ' +\n 'font-family=\"Arial, sans-serif\" ' +\n 'text-anchor=\"middle\" ' +\n 'dominant-baseline=\"middle\" ' +\n 'font-size=\"50\" ' +\n 'font-weight=\"bold\" ' +\n 'fill=\"' + color + '\">' +\n displayText +\n '<\/text>' +\n '<!-- 提示文本 -->' +\n '<text x=\"87\" y=\"95\" ' +\n 'font-family=\"Arial, sans-serif\" ' +\n 'text-anchor=\"middle\" ' +\n 'dominant-baseline=\"middle\" ' +\n 'font-size=\"9\" ' +\n 'fill=\"' + color + '\" ' +\n 'opacity=\"0.7\">' +\n '刷新查看' +\n '<\/text>' +\n '<\/svg>';\n }\n\n var encodedSvg = this.java.base64Encode(svg);\n return 'data:image\/svg+xml;base64,' + encodedSvg + ',{\"js\":\"showCmt(\\'' + encodedUrl + '\\', \\'' + sources + '\\')\",\"style\":\"text\"}';\n}\n\n\n\nfunction cleanHTML(html) {\n let result = html\n .replace(\/<header[^>]*>[\\s\\S]*?<\\\/header>\/gi, \"\")\n .replace(\/<div class=\"tt-title\"[^>]*>[\\s\\S]*?<\\\/div>\/gi, \"\")\n .replace(\/<(?!\\\/?p\\b|\\\/?img\\b)[^>]+>\/gi, \"\");\n result = result.replace(\/<\\\/?p[^>]*>\/g, \"\\n\");\n return result.replace(\/\\n+\/g, \"\\n\").trim();\n}\nvar javaImport = new JavaImporter();\njavaImport.importClass(\n Packages.android.util.Base64,\n Packages.java.lang.String,\n Packages.java.net.URL,\n Packages.okhttp3.HttpUrl\n);\nwith(javaImport) {\n function btoa(data) {\n return Base64.encodeToString(String(data).getBytes(\"UTF-8\"), 2);\n }\n\n function getSubDomain(url) {\n let baseUrl = getBaseUrl(url);\n if (!baseUrl) {\n return url;\n }\n try {\n let mURL = URL(baseUrl);\n let host = mURL.host;\n if (isIPAddress(host)) return host;\n return HttpUrl.parse(baseUrl).topPrivateDomain() || host;\n } catch (e) {\n this.java.log(e);\n return baseUrl;\n }\n }\n\n function getDomain(url) {\n let baseUrl = getBaseUrl(url);\n if (!baseUrl) {\n return url;\n }\n try {\n return URL(baseUrl).host;\n } catch (e) {\n return baseUrl;\n }\n }\n \/**\n * 移除cookie\n *\/\n function removeCookie(url) {\n const {\n cookie\n } = this;\n cookie.removeCookie(url);\n let domains = [getDomain(url), getSubDomain(url)];\n domains.forEach((domain) => {\n cookie.removeCookie(domain);\n });\n }\n}\n\nfunction getBaseUrl(url) {\n if (!url) {\n return null;\n }\n url = String(url);\n if (url.match(\/https?:\\\/\\\/\/i)) {\n var index = url.indexOf(\"\/\", 9);\n return index == -1 ? url : url.substring(0, index);\n }\n return null;\n}\n\nfunction isIPv4Address(ip) {\n ip = String(ip);\n let parts = ip.split(\".\");\n if (parts.length !== 4) return false;\n\n for (let part of parts) {\n if (!\/^\\d+$\/.test(part)) return false; \/\/ 必须是数字\n if (part.length > 1 && part[0] === \"0\") return false; \/\/ 禁止前导零\n let num = parseInt(part, 10);\n if (num < 0 || num > 255) return false; \/\/ 范围检查\n }\n return true;\n}\n\nfunction isIPv6Address(ip) {\n ip = String(ip);\n \/\/ 处理双冒号(最多出现一次)\n if (ip.includes(\":::\")) return false;\n let doubleColonCount = (ip.match(\/::\/g) || []).length;\n if (doubleColonCount > 1) return false;\n\n \/\/ 分割成组\n let groups = ip.split(\":\");\n let validGroupCount = 8;\n let actualGroupCount = groups.filter((g) => g !== \"\").length;\n\n \/\/ 验证组数\n if (doubleColonCount === 1) {\n if (actualGroupCount > validGroupCount - 1) return false;\n } else {\n if (groups.length !== validGroupCount) return false;\n }\n\n \/\/ 验证每组内容\n for (let group of groups) {\n if (group === \"\") continue; \/\/ 跳过空组(双冒号部分)\n if (!\/^[0-9a-fA-F]{1,4}$\/.test(group)) return false; \/\/ 1-4位十六进制\n }\n return true;\n}\n\nfunction isIPAddress(input) {\n return isIPv4Address(input) || isIPv6Address(input);\n}\n\nfunction getSessionId(cookieString) {\n const match = cookieString.match(\/sessionid=([^;]+)\/);\n return match ? match[1] : null;\n}\n\nfunction getKey(key) {\n let parts = key.split(\";\");\n for (let part of parts) {\n if (part.includes(\"qttoken\")) {\n return part.split(\"=\")[1];\n }\n }\n return \"\";\n}\n\nfunction getFanqieCookie() {\n const {\n cookie\n } = this;\n try {\n return String(cookie.getCookie('fanqienovel.com') || java.getCookie('fanqienovel.com') || '');\n } catch (e) {\n return '';\n }\n}\n\n\nfunction paraForiOS(html, sources) {\n return html.replace(\n \/<p>(.*?)(?:<comment ident=\"([^\"]*)\" count=\"([^\"]*)\" \\\/>)?<\\\/p>\/g,\n function(match, text, url, count) {\n if (url && count) {\n const encodedUrl = url.replace(\/&\/g, '&');\n return `<span rs-native>${text}<comment count=\"${count}\" onPress=\"java.startBrowser('${encodedUrl}','${sources}段评')\"><\/span>`;\n } else {\n return `<span rs-native>${text}<\/span>`;\n }\n }\n );\n}",
"lastUpdateTime": "1763266128597",
"loginUi": "[{\n \"name\": \"邮箱\",\n \"type\": \"text\"\n },\n {\n \"name\": \"密码\",\n \"type\": \"password\"\n },\n {\n \"name\": \"♥登录书源\",\n \"type\": \"button\",\n \"action\": \"login(true)\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"🍅番茄登录\",\n \"type\": \"button\",\n \"action\": \"fq_login()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n },\n {\n \"name\": \"🏝用户后台\/注册\",\n \"type\": \"button\",\n \"action\": \"loginqt()\",\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 \"name\": \"🔮 检测登录\",\n \"type\": \"button\",\n \"action\": \"checkStatus()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n\n }, {\n \"name\": \" 🗑 清除设备\",\n \"type\": \"button\",\n \"action\": \"clearDevice()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n\n }, {\n \"name\": \"📑更少简介\",\n \"type\": \"button\",\n \"action\": \"set_info()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"📝段评开关\",\n \"type\": \"button\",\n \"action\": \"paracomment('fqpara')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"👨🦱男频发现\",\n \"type\": \"button\",\n \"action\": \"set_source_type('男频')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"👩🦰女频发现\",\n \"type\": \"button\",\n \"action\": \"set_source_type('女频')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"☕打赏享福利\",\n \"type\": \"button\",\n \"action\": \"vip()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"❇️ 更新书源\",\n \"type\": \"button\",\n \"action\": \"renderVersionPage()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"🗂当前模式\",\n \"type\": \"button\",\n \"action\": \"get_media()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n }, {\n \"name\": \"📖小说模式\",\n \"type\": \"button\",\n \"action\": \"set_media('小说')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"🔊听书模式\",\n \"type\": \"button\",\n \"action\": \"set_media('听书')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"🏞漫画模式\",\n \"type\": \"button\",\n \"action\": \"set_media('漫画')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"🖲短剧模式\",\n \"type\": \"button\",\n \"action\": \"set_media('短剧')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"🎚切换服务器\",\n \"type\": \"button\",\n \"action\": \"set_server()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"♻️检测当前服务器\",\n \"type\": \"button\",\n \"action\": \"checkNet()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"⚕️本地\/服务器 \",\n \"type\": \"button\",\n \"action\": \"get_proxy()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"📌永久发布页📌\",\n \"type\": \"button\",\n \"action\": \"api()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"↓↓下方可切换来源用于搜索\/发现页↓↓\",\n \"type\": \"button\",\n \"action\": \"get_media()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n }, {\n \"name\": \"✨️网友推荐\",\n \"type\": \"button\",\n \"action\": \"set_source('推荐')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"💖我来推荐\",\n \"type\": \"button\",\n \"action\": \"put_book()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"全部\",\n \"type\": \"button\",\n \"action\": \"set_source('全部')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('番茄')\",\n 'name': '番茄',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('69书吧')\",\n 'name': '69书吧',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('七猫')\",\n 'name': '七猫',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('百度')\",\n 'name': '百度',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('起点')\",\n 'name': '起点(第三方)',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('得间')\",\n 'name': '得间',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('塔读')\",\n 'name': '塔读',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('书旗')\",\n 'name': '书旗',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('QQ')\",\n 'name': 'QQ',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('猫眼')\",\n 'name': '猫眼',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('搜书神器')\",\n 'name': '搜书神器',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('得奇')\",\n 'name': '得奇',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('伪69')\",\n 'name': '伪69',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('爱下电子书')\",\n 'name': '爱下电子书',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('小米')\",\n 'name': '小米',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('星星小说')\",\n 'name': '星星小说',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('笔趣阁22')\",\n 'name': '笔趣阁22',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('顶点')\",\n 'name': '顶点',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('幻梦轻小说')\",\n 'name': '幻梦轻小说',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('江湖')\",\n 'name': '江湖',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('歪瑞古德')\",\n 'name': '歪瑞古德漫画',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('河马')\",\n 'name': '河马短剧',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('知乎')\",\n 'name': '知乎',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('vip')\",\n 'name': '下方为VIP专属书源(点击此处搜所有vip)',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n }, {\n 'action': \"set_source('喜马拉雅')\",\n 'name': '喜马拉雅',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('懒人听书')\",\n 'name': '懒人听书',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('svip')\",\n 'name': '下方为SVIP专属书源(点击此处搜所有svip)',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n }, {\n 'action': \"set_source('超会专属短剧')\",\n 'name': '超会专属短剧',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('超会专属小说')\",\n 'name': '超会专属小说',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('番薯小说')\",\n 'name': '番薯小说',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('QQ阅读')\",\n 'name': 'QQ(会员书籍免费)',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('阅友小说')\",\n 'name': '阅友小说',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('好看漫画')\",\n 'name': '好看漫画',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('全本小说')\",\n 'name': '全本小说',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('神书网')\",\n 'name': '神书网',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('读全本')\",\n 'name': '读全本',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('八一中文网')\",\n 'name': '八一中文网',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('斋书苑')\",\n 'name': '斋书苑',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('101看书')\",\n 'name': '101看书',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('万相书城')\",\n 'name': '万相书城',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('轻小说文库')\",\n 'name': '轻小说文库',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('轻之文库')\",\n 'name': '轻之文库',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('飞速中文')\",\n 'name': '飞速中文',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('中华典藏')\",\n 'name': '中华典藏',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('毒舌影视')\",\n 'name': '毒舌影视',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('福利小说')\",\n 'name': '福利小说',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n 'action': \"set_source('福利漫画')\",\n 'name': '福利漫画',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"自定义搜索源(多个用英文,分割)\",\n \"type\": \"text\"\n }, {\n \"name\": \"听书Ai音色填写后点击右上角✔\",\n \"type\": \"text\"\n }, {\n \"name\": \"自定义服务器(可不填)\",\n \"type\": \"text\"\n }, {\n \"name\": \"自定义评论颜色(可不填)\",\n \"type\": \"text\"\n }, {\n \"name\": \"段评气泡样式\",\n \"type\": \"text\"\n }, {\n \"name\": \"手动填写番茄token(可不填)\",\n \"type\": \"text\"\n }\n]",
"loginUrl": "\/\/ 当前书源版本号,切勿修改,否则影响更新的识别\nconst localVersion = '4.11.9';\n\nfunction login(flag) {\n if (flag == undefined) {\n result = JSON.parse(source.getLoginInfo())\n } else {\n java.longToast(\"\\n\\n💞正在登录中...\")\n putLoginInfo(JSON.stringify(result))\n }\n let base_url = getArgument('server')\n let zdyserver = String(result['自定义服务器(可不填)']);\n if (zdyserver.includes('http')) {\n setArgument('server', zdyserver);\n if (getKey(String(cookie.getCookie(base_url)))) {\n let cookies = cookie.getCookie(base_url)\ntry{removeCookie(base_url)} catch(e){cookie.removeCookie(base_url)}\n cookie.setCookie(zdyserver, cookies)\n }\n java.toast(`\\n\\n当前服务器为自定义服务器\\n${zdyserver}\\n\\n切换服务器请先清空服务器地址中的数据`);\n }\n let zdytone_id = String(result['听书Ai音色填写后点击右上角✔'] || '');\n if (zdytone_id) {\n setArgument('tone_id', zdytone_id);\n } else {\n setArgument('tone_id', '默认音色');\n }\n let plcolor = String(result['自定义评论颜色(可不填)'] || '#000000');\n if (plcolor) {\n setArgument('plcolor', plcolor);\n } else {\n setArgument('plcolor', '#000000');\n }\n let zdysources = String(result['自定义搜索源(多个用英文,分割)'] || '');\n if (zdysources) {\n setArgument('source', zdysources);\n };\n base_url = getArgument('server')\n let register_email = String(result['邮箱'])\n let password = String(result['密码'])\n let key = String(result['密钥'] || '')\n\n \/\/java.log(cookie.getCookie(base_url))\n if ((register_email && password || key) && !String(getKey(String(cookie.getCookie(base_url))))) {\n try{removeCookie(base_url)} catch(e){cookie.removeCookie(base_url)}\n let deviceKey = '';\n try {\n deviceKey = java.deviceID();\n } catch (e) {\n deviceKey = java.androidId();\n };\n let deviceId = deviceKey;\n if (register_email && password) {\n let options = JSON.stringify({\n method: 'POST',\n headers: {\n 'Content-Type': 'application\/json'\n },\n body: JSON.stringify({\n register_email: result['邮箱'],\n password: result['密码']\n })\n })\n try {\n let data = JSON.parse(java.ajax(`${base_url}\/login_api,${options}`))\n if (data.code == 0) {\n \/\/java.toast(deviceId)\n java.toast(\"\\n\\n✅️登录成功\")\n cookie.setCookie(base_url, `qttoken=${data.key};deviceId=${deviceId}`)\n result['密钥'] = data.key\n putLoginInfo(JSON.stringify(result))\n } else {\n java.toast('\\n\\n💔' + data.msg || \"登录失败,请重试!\")\n }\n } catch (e) {\n java.toast(\"\\n\\n💔登录失败,请重试!\\n\" + e.message)\n }\n } else {\n cookie.setCookie(base_url, `qttoken=${key};deviceId=${deviceId}`)\n let res = java.ajax(`${base_url}\/user_api,{\"method\":\"POST\",\"headers\":{\"cookie\":\"${cookie.getCookie(base_url)}\"}}`)\n try {\n res = JSON.parse(res)\n if (res.id != undefined) {\n java.toast('\\n\\n密钥登录成功')\n result['邮箱'] = res.email\n putLoginInfo(JSON.stringify(result))\n } else {\n throw new Error()\n }\n } catch (e) {\n java.log(e)\n java.toast(\"\\n\\n💔登录失败\")\n }\n }\n } else if (flag && String(getKey(String(cookie.getCookie(base_url))))) {\n java.toast(\"\\n\\n当前✅️已登录,请🚫退出登录后重新登录\");\n \/\/checkStatus();\n } else if (flag) {\n java.toast(\"\\n\\n⛔️请先填写邮箱和密码\");\n }\n}\n\n\/\/ 检测服务器\nfunction checkNet() {\n let url = getArgument('server');\n java.longToast(`\\n\\n♻️正在检测:${url}\\n请稍等~`);\n let date1 = new Date().getTime();\n let html = java.ajax(url + '\/login');\n let date2 = new Date().getTime();\n let t = date2 - date1;\n let c = String(html).indexOf('晴天');\n let code = 1;\n let time = t \/ 1000 + 's';\n let logTime = '【' + url + '】\\n┋┋\\n' + '解析时间:' + time;\n if (c == -1 || t > 5000) {\n java.longToast('\\n💔【访问失败提示】\\n' + '┏┅━┅━┅━┅━┅┅━┅━┅┓\\n┋┋\\n' + logTime + '\\n┋┋\\n♣️当前接口无法访问(可能被墙)♣️\\n┋┋\\n请切换其他接口\/切换网络环境\\n┋┋' + '\\n┗┅━┅━┅━┅━┅┅━┅━┅┛');\n } else if (t < 1000) {\n java.longToast('\\n💖【网络环境优良】\\n' + '┏┅━┅━┅━┅━┅┅━┅━┅┓\\n┋┋\\n' + logTime + '\\n┋┋\\n❤️延迟低,推荐使用此接口❤️\\n┋┋\\n网络环境优良,请继续保持状态\\n┋┋' + '\\n┗┅━┅━┅━┅━┅┅━┅━┅┛');\n } else if (t >= 1000 && t < 2000) {\n java.longToast('\\n💛【网络环境一般】\\n' + '┏┅━┅━┅━┅━┅┅━┅━┅┓\\n┋┋\\n' + logTime + '\\n┋┋\\n♦️延迟一般,勉强可使用♦️\\n┋┋\\n请切换其他接口或切换网络环境\\n┋┋' + '\\n┗┅━┅━┅━┅━┅┅━┅━┅┛');\n } else if (t >= 2000 && t < 5000) {\n java.longToast('\\n💔【网络环境堪忧】\\n' + '┏┅━┅━┅━┅━┅┅━┅━┅┓\\n┋┋\\n' + logTime + '\\n┋┋\\n♠延迟过高,不建议使用♠\\n┋┋\\n请切换其他接口或切换网络环境\\n┋┋' + '\\n┗┅━┅━┅━┅━┅┅━┅━┅┛');\n }\n}\n\nfunction isVips(res) {\n let isVIP = '';\n let vipEndTime = res.vip_end_time;\n let formattedDate = '';\n\n if (vipEndTime && vipEndTime !== 0) {\n let date = new Date(vipEndTime * 1000);\n formattedDate = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;\n let currentTime = Math.floor(Date.now() \/ 1000);\n let remainingDays = Math.ceil((vipEndTime - currentTime) \/ (24 * 60 * 60));\n if (currentTime > vipEndTime) {\n isVIP = `${res.vip_level === 1 ? \"VIP\" : \"SVIP\"} (已过期)`;\n } else if (remainingDays <= 7) {\n isVIP = `${res.vip_level === 1 ? \"VIP\" : \"SVIP\"} 剩余${remainingDays}天`;\n } else {\n if (vipEndTime < 1912946812) {\n isVIP = `${res.vip_level === 1 ? \"VIP\" : \"SVIP\"}(${formattedDate})`;\n } else {\n isVIP = `${res.vip_level === 1 ? \"VIP\" : \"SVIP\"} (永久)`;\n }\n }\n } else {\n isVIP = '您尚未开通VIP';\n }\n return isVIP;\n}\n\nfunction checkStatus() {\n java.longToast('\\n\\n♻️检测中...');\n let base_url = getArgument('server')\n let res = java.ajax(`${base_url}\/user_api,{\"method\":\"POST\",\"headers\":{\"cookie\":\"${cookie.getCookie(base_url)}\"}}`)\n try {\n res = JSON.parse(res)\n if (res.id != undefined) {\n result['邮箱'] = res.email\n putLoginInfo(JSON.stringify(result))\n let devices\n try {\n devices = JSON.parse(res.device).length;\n } catch (e) {\n devices = res.device ? 1 : 0;\n }\n let isVip = isVips(res);\n tips = `\n┏┅┅┅┅┅┅┱┄┄┄┄┄┄┄┄┄┄┐\n 🧢昵称 ${res.nickname.padEnd(20,\"\\t\") || \"未设置\".padEnd(20,\"\\t\")}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n ✉️邮箱 ${res.email.replace(\/(.{3}).*?@\/,\"$1***@\").padEnd(20,\"\\t\")}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n 🔑密钥 ${(`${res.user_key.substring(0,4)}***${res.user_key.slice(-4)}`).padEnd(20,\"\\t\")}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n 📅注册时间 ${java.timeFormat(res.register_time*1000).padEnd(20,\"\\t\")}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n 🗒️今日阅读 ${(java.timeFormat(new Date()).slice(0,10)==java.timeFormat(res.last_read_time * 1000).slice(0,10)?res.day_read_count:0).toString().padEnd(20,\"\\t\")}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n 📚累计阅读 ${res.all_read_count.toString().padEnd(20,\"\\t\")}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n 🕓最后阅读 ${(res.last_read_time != 0?java.timeFormat(res.last_read_time * 1000):'未阅读').padEnd(20,\"\\t\")}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n 📱关联设备 ${devices.toString().padEnd(20,\"\\t\")}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n 👑会员状态 ${isVip.padEnd(20,\"\\t\")}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n 🚫封禁状态 ${res.is_banned?'已封禁':'正常 '} \n┗┅┅┅┅┅┅┹┄┄┄┄┄┄┄┄┄┄┘\n`\n java.log(tips)\n java.longToast(tips)\n } else {\n throw new Error(res.msg)\n }\n } catch (e) {\n \/\/java.log(e)\n java.toast(\"\\n检测登录失败\\n\" + e.message)\n }\n}\n\nfunction clearDevice() {\n let base_url = getArgument('server')\n let res = java.ajax(`${base_url}\/clear,{\"method\":\"POST\",\"headers\":{\"cookie\":\"${cookie.getCookie(base_url)}\"}}`)\n java.toast(res.code === 0 ? \"\\n\\n📴设备清除成功\" : res.msg)\n Packages.java.lang.Thread.sleep(500)\n checkStatus()\n}\n\/\/ 保存登录UI信息\nfunction putLoginInfo(info) {\n try {\n let key = java.androidId()\n let encodeStr = Packages.android.util.Base64.encodeToString(java.createSymmetricCrypto(\"AES\", key).encrypt(info), 2)\n cache.put(`userInfo_${source.getKey()}`, encodeStr)\n return true\n } catch (e) {\n java.log(e)\n return source.putLoginInfo(info)\n }\n}\n\n\/\/ 填写密钥\nfunction loginqt() {\n java.startBrowserAwait(getArgument('server') + '\/login', '登录晴天小说书源');\n}\n\n\/\/登录番茄\nfunction fq_login() {\n try {\n java.startBrowserAwait(\"https:\/\/fanqienovel.com\/\", \"登录\")\n } catch (e) {\n java.toast(e)\n }\n try {\n cookie.removeCookie(\"snssdk.com\")\n } catch (e) {}\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) {\n java.log(e)\n }\n if (!cookie_ || cookie_ == \"sessionid=\" || !user) {\n java.toast(\"未获取到登录凭据,登录失败\")\n return false\n }\n java.toast(\"\\n\\n欢迎 \" + user + \"\\n登录成功!\")\n return true\n}\n\n\/\/退出登录\nfunction logout() {\n cookie.removeCookie(\"fanqienovel.com\");\n cookie.removeCookie(\"snssdk.com\");\n cookie.removeCookie(\"69shuba.com\");\n \/*\n let servers = host\n for (let server of servers) {\n \ttry{removeCookie(server )} catch(e){cookie.removeCookie(server )}\n }\n *\/\ntry{removeCookie(getArgument('server'))} catch(e){cookie.removeCookie(getArgument('server'))}\n java.toast(\"退出登录成功\");\n}\n\n\/\/获取参数\nfunction getArgument(key) {\n let open_argument = source.getVariable();\n open_argument = getArguments(open_argument, '');\n return open_argument[key];\n}\n\n\/\/设置参数\nfunction setArgument(key, value) {\n let open_argument = source.getVariable();\n open_argument = getArguments(open_argument, '');\n open_argument[key] = value;\n open_argument = JSON.stringify(open_argument);\n source.setVariable(open_argument);\n return open_argument;\n}\n\n\/\/ 设置本地or云端访问\nfunction get_proxy() {\n let proxy = getArgument('proxy');\n if (proxy == '本地') {\n setArgument('proxy', '云端');\n java.longToast('\\n所有数据采用\\n\\n服务器网络访问\\n\\n如果发现用不了,请切换本地网络访问,如69书吧');\n } else {\n setArgument('proxy', '本地');\n java.longToast('\\n所有数据采用\\n\\n本地网络访问\\n\\n如果发现用不了,请开启网络代理,如69书吧');\n }\n}\n\n\n\/\/设置男女频\nfunction set_source_type(source_type) {\n setArgument(\"source_type\", source_type);\n java.toast(\"\\n发现页已设置为:\" + source_type);\n}\n\n\/\/首页\nfunction api() {\n java.startBrowserAwait('http:\/\/vip.gyks.cf', \"首页\");\n}\n\n\/\/打赏\nfunction vip() {\n java.startBrowserAwait(getArgument('server') + '\/coffee', \"喝咖啡\");\n}\n\nvar server = getArgument('server');\n\n\/\/设置搜索媒体\nfunction set_media(media) {\n const mediaConfig = {\n '喜马拉雅': ['听书'],\n '番茄': '*',\n '福利小说': ['小说'],\n '如漫画': ['漫画'],\n '包子漫画': ['漫画'],\n '九妖漫画': ['漫画'],\n '绅士漫画': ['漫画'],\n '福利漫画': ['漫画'],\n '好看漫画': ['漫画'],\n '六月听书': ['听书'],\n '海洋听书': ['听书'],\n '七猫': ['小说', '听书', '短剧'],\n '河马': ['短剧'],\n '超会专属短剧': ['短剧'],\n '歪瑞古德': ['漫画'],\n '毒舌影视': ['短剧'],\n '全部': '*', \/\/ 允许所有模式\n '默认': ['小说']\n };\n\n const source = getArgument('source');\n const allowedModes = mediaConfig[source] || mediaConfig['默认'];\n let targetMedia = mediaConfig['默认'][0];\n let isAllowed = false;\n\n if (allowedModes === '*' || allowedModes.includes(media)) {\n targetMedia = media;\n isAllowed = true;\n } else if (Array.isArray(allowedModes)) {\n targetMedia = allowedModes[0];\n }\n\n const message = isAllowed ?\n `\\n\\n已切换至:${targetMedia}\\n请重新搜索书籍!` :\n `\\n\\n目前${source}:不支持【${media}】模式!\\n已自动切换至:${targetMedia}`;\n\n setArgument('media', targetMedia);\n java.toast(message);\n}\n\n\/\/获取搜索媒体\nfunction get_media() {\n let media = getArgument('media');\n let source = getArgument('source');\n if (media == '') {\n media = '全部';\n }\n var tishi = '\\n\\n当前服务器:' + getArgument('server')\n java.longToast(`\\n\\n当前使用源:${source}-${media}${tishi}`);\n}\n\n\n\/\/设置服务器\nfunction set_server() {\n putLoginInfo(JSON.stringify(result))\n let zdyserver;\n let base_url = getArgument('server')\n try {\n zdyserver = String(result['自定义服务器(可不填)']);\n if (zdyserver.includes('http')) {\n setArgument('server', zdyserver);\n if (getKey(String(cookie.getCookie(base_url)))) {\n let cookies = cookie.getCookie(base_url)\n try{removeCookie(base_url)} catch(e){cookie.removeCookie(base_url)}\n cookie.setCookie(zdyserver, cookies)\n }\n java.toast(`\\n\\n当前服务器为自定义服务器\\n${zdyserver}\\n\\n切换服务器请先清空服务器地址中的数据`);\n } else {\n zdyserver = '';\n }\n } catch (error) {\n zdyserver = '';\n }\n if (!zdyserver) {\n const servers = host\n const currentServer = getArgument('server') || '';\n const currentIndex = servers.indexOf(currentServer);\n\n const nextIndex = currentIndex >= 0 ? (currentIndex + 1) % servers.length : 0;\n const nextServer = servers[nextIndex];\n\n setArgument('server', nextServer);\n if (getKey(String(cookie.getCookie(currentServer)))) {\n let cookies = cookie.getCookie(currentServer)\ntry{removeCookie(currentServer)} catch(e){cookie.removeCookie(currentServer)}\n cookie.setCookie(nextServer, cookies)\n }\n java.longToast(`\\n\\n服务器【${nextIndex+1}】:${nextServer}`);\n }\n}\n\n\/\/获取音色\nvar tone_id = getArgument('tone_id');\n\nfunction get_tone_id(arg) {\n var datadist = {\n \"0\": \"默认音色\",\n \"-1\": \"阅读模式\",\n \"-2\": \"漫画模式\",\n \"51\": \"多人发音\",\n \"1\": \"甜美少女\",\n \"2\": \"清亮青叔\",\n \"5\": \"开朗青年\",\n \"6\": \"温柔淑女\",\n \"4\": \"成熟大叔\",\n \"74\": \"大叔升级\",\n \"30\": \"优雅御姐\"\n };\n var tone_id = datadist[arg] || arg;\n var tishi = '\\n\\n当前音色:' + tone_id;\n java.toast(tishi);\n}\n\n\/\/设置音色\nfunction set_tone_id(mode, name) {\n putLoginInfo(JSON.stringify(result))\n let zdytone_id;\n try {\n zdytone_id = String(result['其他音色填写后点击右上角✔']);\n if (zdytone_id) {\n setArgument('tone_id', zdytone_id);\n java.toast(`\\n\\n当前音色为自定义音色\\n${zdytone_id}\\n\\n切换音色请先清空音色输入框中的数据`);\n } else {\n zdytone_id = '';\n }\n } catch (error) {\n zdytone_id = '';\n }\n if (!zdytone_id) {\n let toast = \"\\n\\n已切换至:\" + name + '\\n\\n切换后需要刷新详情页';\n setArgument('tone_id', mode);\n java.toast(toast);\n }\n}\n\n\/\/设置来源\nfunction set_source(sources) {\n let zdysources = String(result['自定义搜索源(多个用英文,分割)'] || '');\n if (zdysources.length > 1 && zdysources != 'undefined') {\n java.toast('\\n\\n请先清空自定义源再设置');\n } else {\n setArgument('source', sources);\n set_media('小说');\n java.toast(`\\n\\n当前来源已切换为:\\n${sources}\\n\\n切换后请重新搜索`);\n }\n}\n\n\/\/ 设置简介\nfunction set_info() {\n var info = getArgument('info');\n if (info == 'on') {\n setArgument('info', 'off');\n java.toast('\\n\\n已恢复详情页详细简介');\n } else {\n setArgument('info', 'on');\n java.toast('\\n\\n已精简详情页简介');\n }\n}\n\n\/\/ 番茄段评\nfunction paracomment() {\n var fqpara = getArgument('fqpara');\n if (fqpara == 'on') {\n setArgument('fqpara', 'off');\n java.longToast('\\n\\n段评已关闭');\n } else {\n setArgument('fqpara', 'on');\n java.longToast(\"\\n\\n段评已开启\\n\\n长按刷新段后面的图片即可\\n\\n如果图片不显示,刷新无反应\\n请更新测试版阅读app\");\n }\n}\n\n\n\/\/ 我要推荐\nfunction put_book() {\n java.startBrowserAwait(getArgument('server') + '\/put_book', '我来推荐');\n}\n\n\/\/ 书源更新\nfunction renderVersionPage() {\n let yd = '';\n let html = `\n<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"UTF-8\" \/>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" \/>\n <title>书源更新<\/title>\n <!-- Font Awesome 图标库 -->\n <link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.4.0\/css\/all.min.css\" \/>\n <style>\n :root {\n --primary-gradient: linear-gradient(135deg, #4e6ef2, #6b2dd8);\n --latest-gradient: linear-gradient(135deg, #8e2de2 0%, #4a00e0 50%, #d4af37 100%);\n --success-color: #28c76f;\n --warning-color: #ff9f43;\n --error-color: #ea5455;\n --text-main: #1f2937;\n --text-secondary: #6b7280;\n --card-bg: #ffffff;\n --border-color: #e5e7eb;\n --light-bg: #f9fafb;\n --shadow: 0 4px 12px rgba(78, 110, 242, 0.1);\n --shadow-hover: 0 6px 18px rgba(78, 110, 242, 0.2);\n --glow-shadow: 0 0 25px rgba(142, 45, 226, 0.5), 0 0 50px rgba(212, 175, 55, 0.3);\n --modal-bg: rgba(31, 41, 55, 0.8);\n --modal-content-bg: #ffffff;\n }\n\n * {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n font-family: 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif;\n }\n\n body {\n background: linear-gradient(135deg, #eef2ff, #f5f7ff);\n color: var(--text-main);\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 16px;\n }\n\n \/* 加载动画 *\/\n .loading-wrapper {\n text-align: center;\n animation: fadeIn 0.3s ease;\n }\n\n .loading-spinner {\n width: 50px;\n height: 50px;\n border: 4px solid rgba(78, 110, 242, 0.3);\n border-top-color: #4e6ef2;\n border-radius: 50%;\n margin: 0 auto 20px;\n animation: spin 1s linear infinite;\n }\n\n .loading-text {\n color: var(--text-main);\n font-size: 16px;\n font-weight: 500;\n }\n\n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n\n @keyframes fadeIn {\n from { opacity: 0; transform: translateY(20px); }\n to { opacity: 1; transform: translateY(0); }\n }\n\n @keyframes slideIn {\n from { opacity: 0; transform: translateY(30px); }\n to { opacity: 1; transform: translateY(0); }\n }\n\n @keyframes pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.7; }\n }\n\n @keyframes gradientAnimation {\n 0% { background-position: 0% 50%; }\n 50% { background-position: 100% 50%; }\n 100% { background-position: 0% 50%; }\n }\n\n @keyframes breathe {\n 0%, 100% { \n transform: scale(1);\n box-shadow: var(--glow-shadow), var(--shadow);\n }\n 50% { \n transform: scale(1.02);\n box-shadow: 0 0 30px rgba(142, 45, 226, 0.6), 0 0 60px rgba(212, 175, 55, 0.4), var(--shadow);\n }\n }\n\n @keyframes shimmer {\n 0% {\n background-position: -200% center;\n }\n 100% {\n background-position: 200% center;\n }\n }\n\n \/* 主容器 *\/\n .container {\n width: 100%;\n max-width: 420px;\n background: var(--card-bg);\n border-radius: 24px;\n overflow: hidden;\n box-shadow: var(--shadow);\n position: relative;\n z-index: 1;\n animation: slideIn 0.5s ease;\n display: none;\n }\n\n \/* 头部 *\/\n .header {\n background: var(--primary-gradient);\n color: #ffffff;\n padding: 24px 16px;\n text-align: center;\n position: relative;\n overflow: hidden;\n }\n\n .header::before {\n content: '';\n position: absolute;\n top: -30px;\n left: -30px;\n width: 80px;\n height: 80px;\n background: rgba(255, 255, 255, 0.15);\n border-radius: 50%;\n }\n\n .header::after {\n content: '';\n position: absolute;\n bottom: -60px;\n right: -60px;\n width: 150px;\n height: 150px;\n background: rgba(255, 255, 255, 0.1);\n border-radius: 50%;\n }\n\n .header h1 {\n font-size: 1.4rem;\n font-weight: 700;\n margin-bottom: 8px;\n position: relative;\n z-index: 2;\n }\n\n .header p {\n font-size: 0.9rem;\n opacity: 0.9;\n line-height: 1.4;\n position: relative;\n z-index: 2;\n }\n\n .header-icon {\n font-size: 48px;\n margin-bottom: 10px;\n display: inline-block;\n animation: bounce 2s ease infinite;\n }\n\n @keyframes bounce {\n 0%, 100% { transform: translateY(0); }\n 50% { transform: translateY(-10px); }\n }\n\n \/* 版本对比 *\/\n .version-comparison {\n display: flex;\n flex-wrap: nowrap;\n gap: 12px;\n padding: 16px;\n margin-top: 8px;\n position: relative;\n z-index: 10;\n }\n\n .version-card {\n flex: 1;\n min-width: 45%;\n background: var(--card-bg);\n border-radius: 16px;\n padding: 28px 16px 16px;\n box-shadow: var(--shadow);\n text-align: center;\n position: relative;\n transition: transform 0.3s ease, box-shadow 0.3s ease;\n overflow: hidden;\n border: 1px solid rgba(120, 130, 240, 0.1);\n }\n\n .version-card:hover {\n transform: translateY(-4px);\n box-shadow: var(--shadow-hover);\n }\n\n .version-card.current-version {\n background: linear-gradient(135deg, #ffffff 0%, #f8f9ff 100%);\n border: 1px solid rgba(78, 110, 242, 0.15);\n }\n\n .version-card.current-version:hover {\n box-shadow: 0 6px 20px rgba(78, 110, 242, 0.15);\n }\n\n .version-card.current-version h3,\n .version-card.current-version .version-number,\n .version-card.current-version .version-date {\n color: var(--text-main);\n }\n\n .version-card.latest-version {\n background: var(--latest-gradient);\n background-size: 300% 300%;\n box-shadow: var(--glow-shadow), var(--shadow);\n color: #fff;\n z-index: 2;\n animation: gradientAnimation 6s ease infinite, breathe 3s ease-in-out infinite;\n position: relative;\n overflow: hidden;\n }\n\n .version-card.latest-version::before {\n content: '';\n position: absolute;\n top: -50%;\n left: -50%;\n width: 200%;\n height: 200%;\n background: linear-gradient(\n 90deg,\n transparent,\n rgba(255, 255, 255, 0.3),\n transparent\n );\n transform: rotate(45deg);\n animation: shimmer 3s infinite;\n }\n\n .version-card.latest-version h3,\n .version-card.latest-version .version-number,\n .version-card.latest-version .version-date {\n color: #fff;\n text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n position: relative;\n z-index: 1;\n }\n\n .version-status {\n position: absolute;\n top: 6px;\n right: 6px;\n padding: 3px 7px;\n font-size: 0.65rem;\n font-weight: 600;\n border-radius: 6px;\n color: #fff;\n line-height: 1.2;\n white-space: nowrap;\n z-index: 2;\n }\n\n .version-card.latest-version .version-status {\n background: rgba(255, 255, 255, 0.25);\n backdrop-filter: blur(5px);\n border: 1px solid rgba(255, 255, 255, 0.3);\n color: #fff;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);\n }\n\n .status-outdated { background: var(--warning-color); }\n .status-latest { background: var(--success-color); }\n .status-invalid { background: var(--error-color); }\n\n .version-card h3 {\n font-size: 0.9rem;\n color: var(--text-secondary);\n margin-bottom: 8px;\n font-weight: 500;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n }\n\n .version-number {\n font-size: 1.25rem;\n font-weight: 700;\n color: var(--text-main);\n margin: 8px 0;\n transition: all 0.3s ease;\n font-family: 'Courier New', monospace;\n }\n\n .version-card.latest-version .version-number {\n font-size: 1.4rem;\n transform: scale(1.05);\n text-shadow: \n 0 2px 4px rgba(0, 0, 0, 0.3),\n 0 0 10px rgba(212, 175, 55, 0.8),\n 0 0 20px rgba(212, 175, 55, 0.5);\n animation: pulse-glow 2s ease-in-out infinite;\n }\n\n @keyframes pulse-glow {\n 0%, 100% {\n text-shadow: \n 0 2px 4px rgba(0, 0, 0, 0.3),\n 0 0 10px rgba(212, 175, 55, 0.8),\n 0 0 20px rgba(212, 175, 55, 0.5);\n }\n 50% {\n text-shadow: \n 0 2px 4px rgba(0, 0, 0, 0.3),\n 0 0 15px rgba(212, 175, 55, 1),\n 0 0 30px rgba(212, 175, 55, 0.7);\n }\n }\n\n .version-date {\n font-size: 0.8rem;\n color: var(--text-secondary);\n margin-top: 4px;\n }\n\n \/* 版本对比指示器 *\/\n .version-indicator {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n z-index: 5;\n width: 32px;\n height: 32px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);\n color: white;\n }\n\n .version-indicator.update-needed {\n background: var(--error-color);\n box-shadow: 0 2px 8px rgba(234, 84, 85, 0.4);\n animation: pulse-indicator 1.5s infinite;\n }\n\n .version-indicator.is-latest {\n background: var(--success-color);\n box-shadow: 0 2px 8px rgba(40, 199, 111, 0.4);\n }\n\n @keyframes pulse-indicator {\n 0% { transform: translate(-50%, -50%) scale(1); }\n 50% { transform: translate(-50%, -50%) scale(1.1); }\n 100% { transform: translate(-50%, -50%) scale(1); }\n }\n\n \/* 内容区 *\/\n .content-container {\n padding: 16px;\n }\n\n \/* 状态提示 *\/\n .status-alert {\n background: var(--card-bg);\n border-radius: 16px;\n box-shadow: var(--shadow);\n margin-bottom: 16px;\n overflow: hidden;\n border: 1px solid rgba(120, 130, 240, 0.1);\n padding: 12px 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 10px;\n font-weight: 500;\n font-size: 14px;\n animation: slideIn 0.5s ease 0.3s backwards;\n }\n\n .status-alert i {\n font-size: 20px;\n }\n\n .status-alert.update-available {\n background: linear-gradient(135deg, #ffeaa7 0%, #fdcb6e 100%);\n color: #d63031;\n box-shadow: 0 4px 15px rgba(253, 203, 110, 0.4);\n }\n\n .status-alert.up-to-date {\n background: linear-gradient(135deg, #55efc4 0%, #00b894 100%);\n color: white;\n box-shadow: 0 4px 15px rgba(0, 184, 148, 0.4);\n }\n\n \/* 更新容器 *\/\n .update-container {\n background: var(--card-bg);\n border-radius: 16px;\n box-shadow: var(--shadow);\n margin-bottom: 16px;\n overflow: hidden;\n border: 1px solid rgba(120, 130, 240, 0.1);\n animation: slideIn 0.5s ease 0.4s backwards;\n }\n\n .update-header {\n background: var(--light-bg);\n padding: 12px 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n border-bottom: 1px solid var(--border-color);\n }\n\n .update-header h2 {\n font-size: 1rem;\n font-weight: 600;\n color: var(--text-main);\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .update-header h2 i {\n color: #4e6ef2;\n }\n\n .update-tag {\n background: rgba(78, 110, 242, 0.1);\n color: #4e6ef2;\n padding: 4px 8px;\n border-radius: 8px;\n font-size: 0.75rem;\n font-weight: 600;\n }\n\n .update-content {\n padding: 16px;\n }\n\n .update-date {\n font-weight: 600;\n color: #4e6ef2;\n margin-bottom: 12px;\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 0;\n border-bottom: 1px dashed #e0e0e0;\n }\n\n .update-text {\n margin: 8px 0;\n position: relative;\n padding-left: 16px;\n line-height: 1.5;\n color: var(--text-main);\n font-size: 0.95rem;\n white-space: pre-wrap;\n word-break: break-word;\n }\n\n .update-text::before {\n content: '•';\n position: absolute;\n left: 0;\n font-weight: bold;\n color: #4e6ef2;\n font-size: 1.2rem;\n line-height: 1;\n }\n\n \/* 历史日志 *\/\n .history-container {\n background: var(--card-bg);\n border-radius: 16px;\n box-shadow: var(--shadow);\n margin-bottom: 16px;\n border: 1px solid rgba(120, 130, 240, 0.1);\n animation: slideIn 0.5s ease 0.5s backwards;\n }\n\n .history-header {\n background: var(--light-bg);\n padding: 12px 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n border-bottom: 1px solid var(--border-color);\n cursor: pointer;\n user-select: none;\n }\n\n .history-header:hover {\n opacity: 0.8;\n }\n\n .history-header h2 {\n font-size: 1rem;\n font-weight: 600;\n color: var(--text-main);\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .history-header h2 i {\n color: #4e6ef2;\n }\n\n .toggle-history {\n background: none;\n border: none;\n color: var(--text-secondary);\n cursor: pointer;\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 0.85rem;\n transition: color 0.2s ease;\n }\n\n .toggle-history:hover {\n color: #4e6ef2;\n }\n\n .history-content {\n padding: 0 16px;\n max-height: 0;\n overflow: hidden;\n transition: max-height 0.4s ease, padding 0.4s ease;\n }\n\n .history-content.expanded {\n max-height: 60vh;\n overflow-y: auto;\n padding: 16px;\n scrollbar-width: thin;\n scrollbar-color: #4e6ef2 #f0f0f0;\n }\n\n .history-content.expanded::-webkit-scrollbar {\n width: 6px;\n }\n\n .history-content.expanded::-webkit-scrollbar-track {\n background: #f0f0f0;\n border-radius: 4px;\n }\n\n .history-content.expanded::-webkit-scrollbar-thumb {\n background: #4e6ef2;\n border-radius: 4px;\n }\n\n .history-content.expanded::-webkit-scrollbar-thumb:hover {\n background: #3a56d0;\n }\n\n .history-item {\n margin-bottom: 16px;\n padding-bottom: 16px;\n border-bottom: 1px dashed var(--border-color);\n }\n\n .history-item:last-child {\n border-bottom: none;\n margin-bottom: 0;\n padding-bottom: 0;\n }\n\n .history-date {\n font-weight: 600;\n color: var(--text-main);\n margin-bottom: 8px;\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 0.9rem;\n background: rgba(78, 110, 242, 0.05);\n padding: 6px 10px;\n border-radius: 6px;\n }\n\n .history-text {\n margin: 8px 0;\n padding-left: 16px;\n line-height: 1.4;\n color: var(--text-secondary);\n position: relative;\n font-size: 0.9rem;\n white-space: pre-wrap;\n word-break: break-word;\n }\n\n .history-text::before {\n content: '•';\n position: absolute;\n left: 0;\n color: #4e6ef2;\n font-weight: bold;\n font-size: 1.2rem;\n line-height: 1;\n }\n\n \/* 按钮组 *\/\n .button-group {\n display: flex;\n flex-direction: column;\n gap: 10px;\n margin-bottom: 16px;\n }\n\n .button {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 10px;\n padding: 14px 28px;\n text-align: center;\n font-size: 1rem;\n border: none;\n border-radius: 12px;\n text-decoration: none;\n background: var(--primary-gradient);\n color: white;\n font-weight: 600;\n transition: all 0.3s ease;\n box-shadow: var(--shadow);\n position: relative;\n overflow: hidden;\n cursor: pointer;\n }\n\n .button i {\n font-size: 1rem;\n }\n\n .button::after {\n content: '';\n position: absolute;\n top: -50%;\n left: -50%;\n width: 200%;\n height: 200%;\n background: rgba(255, 255, 255, 0.1);\n transform: rotate(30deg);\n transition: all 0.6s ease;\n pointer-events: none;\n }\n\n .button:hover {\n transform: translateY(-3px);\n box-shadow: var(--shadow-hover);\n }\n\n .button:hover::after {\n transform: rotate(30deg) translate(20%, 20%);\n }\n\n .button:active {\n transform: scale(0.95);\n }\n\n \/* 错误状态 *\/\n .error-state {\n text-align: center;\n padding: 40px 20px;\n color: var(--text-main);\n }\n\n .error-icon {\n font-size: 64px;\n margin-bottom: 20px;\n color: var(--error-color);\n }\n\n .error-text {\n font-size: 16px;\n line-height: 1.6;\n margin-bottom: 20px;\n }\n\n .retry-button {\n background: var(--primary-gradient);\n color: white;\n padding: 12px 30px;\n border-radius: 12px;\n border: none;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.3s ease;\n font-size: 14px;\n box-shadow: var(--shadow);\n }\n\n .retry-button:hover {\n transform: translateY(-2px);\n box-shadow: var(--shadow-hover);\n }\n\n .retry-button:active {\n transform: scale(0.95);\n }\n\n \/* 装饰元素 *\/\n .decoration {\n position: absolute;\n z-index: 0;\n pointer-events: none;\n }\n\n .decoration.circle {\n width: 120px;\n height: 120px;\n border-radius: 50%;\n background: rgba(107, 45, 216, 0.05);\n top: 10%;\n left: 10%;\n }\n\n .decoration.square {\n width: 80px;\n height: 80px;\n transform: rotate(45deg);\n background: rgba(78, 110, 242, 0.05);\n bottom: 10%;\n right: 10%;\n }\n\n \/* 响应式 *\/\n @media (max-width: 768px) {\n body {\n padding: 12px;\n }\n\n .container {\n max-width: 100%;\n border-radius: 20px;\n }\n\n .header {\n padding: 20px 15px;\n }\n\n .header h1 {\n font-size: 1.3rem;\n }\n\n .header-icon {\n font-size: 40px;\n }\n\n .version-comparison {\n flex-direction: row;\n flex-wrap: nowrap;\n gap: 10px;\n padding: 12px;\n margin-top: 6px;\n overflow-x: auto;\n }\n\n .version-card {\n min-width: 45%;\n padding: 26px 12px 12px;\n }\n\n \/* 移动端减弱呼吸动效 *\/\n .version-card.latest-version {\n animation: gradientAnimation 6s ease infinite;\n }\n\n .version-status {\n top: 5px;\n right: 5px;\n padding: 2px 5px;\n font-size: 0.6rem;\n }\n\n .version-number {\n font-size: 1.1rem;\n }\n\n .version-card.latest-version .version-number {\n font-size: 1.2rem;\n }\n\n .update-header h2, .history-header h2 {\n font-size: 0.9rem;\n }\n\n .button {\n padding: 12px 24px;\n font-size: 0.95rem;\n }\n\n .history-content.expanded {\n max-height: 50vh;\n -webkit-overflow-scrolling: touch;\n }\n }\n\n @media (max-width: 380px) {\n .header h1 {\n font-size: 1.2rem;\n }\n\n .version-number {\n font-size: 1rem;\n }\n\n .version-card.latest-version .version-number {\n font-size: 1.1rem;\n }\n\n .button {\n padding: 11px;\n font-size: 0.9rem;\n }\n }\n <\/style>\n<\/head>\n<body>\n <div class=\"decoration circle\"><\/div>\n <div class=\"decoration square\"><\/div>\n\n <div id=\"loading\" class=\"loading-wrapper\">\n <div class=\"loading-spinner\"><\/div>\n <div class=\"loading-text\"><i class=\"fas fa-search\"><\/i> 正在检查更新...<\/div>\n <\/div>\n\n <div class=\"container\" id=\"container\">\n <div class=\"header\">\n <div class=\"header-icon\"><i class=\"fas fa-book\"><\/i><\/div>\n <h1>晴天书源更新<\/h1>\n <p>请使用阅读测试版,不支持正式版<\/p>\n <\/div>\n\n <div class=\"version-comparison\">\n <div class=\"version-card current-version\">\n <div class=\"version-status status-outdated\" id=\"currentStatus\">待检查<\/div>\n <h3><i class=\"fas fa-cube\"><\/i> 当前版本<\/h3>\n <div class=\"version-number\" id=\"currentVersion\">-<\/div>\n <div class=\"version-date\">您的当前版本<\/div>\n <\/div>\n\n <div class=\"version-indicator update-needed\" id=\"versionIndicator\" style=\"display: none;\">\n <i class=\"fas fa-arrow-right\"><\/i>\n <\/div>\n\n <div class=\"version-card latest-version\">\n <div class=\"version-status status-latest\" id=\"latestStatus\">最新版本<\/div>\n <h3><i class=\"fas fa-star\"><\/i> 最新版本<\/h3>\n <div class=\"version-number\" id=\"latestVersion\">-<\/div>\n <div class=\"version-date\">可用最新版本<\/div>\n <\/div>\n <\/div>\n\n <div class=\"content-container\">\n <div class=\"status-alert\" id=\"statusAlert\" style=\"display: none;\"><\/div>\n\n <div id=\"latestLogContainer\" style=\"display: none;\">\n <div class=\"update-container\">\n <div class=\"update-header\">\n <h2><i class=\"fas fa-bolt\"><\/i> 最新更新<\/h2>\n <div class=\"update-tag\">最新发布<\/div>\n <\/div>\n <div class=\"update-content\">\n <div class=\"update-date\" id=\"latestLogDate\"><\/div>\n <div class=\"update-text\" id=\"latestLogContent\"><\/div>\n <\/div>\n <\/div>\n <\/div>\n\n <div class=\"button-group\" id=\"buttonGroup\" style=\"display: none;\"><\/div>\n\n <div class=\"history-container\" id=\"logs\" style=\"display: none;\">\n <div class=\"history-header\" onclick=\"toggleLogs()\">\n <h2><i class=\"fas fa-history\"><\/i> 历史更新 <span id=\"historyCount\"><\/span><\/h2>\n <button class=\"toggle-history\" id=\"toggleButton\">\n <span id=\"toggleText\">展开历史<\/span>\n <i class=\"fas fa-chevron-down\" id=\"toggleIcon\"><\/i>\n <\/button>\n <\/div>\n <div class=\"history-content\" id=\"logList\"><\/div>\n <\/div>\n <\/div>\n <\/div>\n\n <script>\n let logsCollapsed = true;\n\n function toggleLogs() {\n logsCollapsed = !logsCollapsed;\n const logList = document.getElementById('logList');\n const toggleText = document.getElementById('toggleText');\n const toggleIcon = document.getElementById('toggleIcon');\n \n if (logsCollapsed) {\n logList.classList.remove('expanded');\n toggleText.textContent = '展开历史';\n toggleIcon.className = 'fas fa-chevron-down';\n } else {\n logList.classList.add('expanded');\n toggleText.textContent = '收起历史';\n toggleIcon.className = 'fas fa-chevron-up';\n }\n }\n\n (async function() {\n const loading = document.getElementById('loading');\n const container = document.getElementById('container');\n const currentVersion = document.getElementById('currentVersion');\n const latestVersion = document.getElementById('latestVersion');\n const currentStatus = document.getElementById('currentStatus');\n const latestStatus = document.getElementById('latestStatus');\n const versionIndicator = document.getElementById('versionIndicator');\n const statusAlert = document.getElementById('statusAlert');\n const buttonGroup = document.getElementById('buttonGroup');\n const latestLogContainer = document.getElementById('latestLogContainer');\n const latestLogDate = document.getElementById('latestLogDate');\n const latestLogContent = document.getElementById('latestLogContent');\n const logsContainer = document.getElementById('logs');\n const logList = document.getElementById('logList');\n const historyCount = document.getElementById('historyCount');\n\n const localVer = '${String(localVersion)}';\n\n \/\/ 统一的服务器配置 - 方便维护\n const serverConfig = {\n main: {\n name: '主线路',\n icon: 'rocket',\n baseUrl: 'https:\/\/sy.gyks.cf',\n downloadPath: '\/download\/%E5%AE%89%E5%8D%93%E9%98%85%E8%AF%BBapp-%E6%99%B4%E5%A4%A9%E8%9E%8D%E5%90%884.0(vip%E5%AE%8C%E5%85%A8%E7%89%88).json'\n },\n backup1: {\n name: '备用线路1',\n icon: 'box',\n baseUrl: 'http:\/\/v1.gyks.cf',\n downloadPath: '\/sy\/download\/%E5%AE%89%E5%8D%93%E9%98%85%E8%AF%BBapp-%E6%99%B4%E5%A4%A9%E8%9E%8D%E5%90%884.0(vip%E5%AE%8C%E5%85%A8%E7%89%88).json'\n },\n backup2: {\n name: '备用线路2',\n icon: 'satellite',\n baseUrl: 'http:\/\/v2.gyks.cf',\n downloadPath: '\/sy\/download\/%E5%AE%89%E5%8D%93%E9%98%85%E8%AF%BBapp-%E6%99%B4%E5%A4%A9%E8%9E%8D%E5%90%884.0(vip%E5%AE%8C%E5%85%A8%E7%89%88).json'\n },\n backup3: {\n name: '备用线路3',\n icon: 'link',\n baseUrl: 'http:\/\/v3.gyks.cf',\n downloadPath: '\/sy\/download\/%E5%AE%89%E5%8D%93%E9%98%85%E8%AF%BBapp-%E6%99%B4%E5%A4%A9%E8%9E%8D%E5%90%884.0(vip%E5%AE%8C%E5%85%A8%E7%89%88).json'\n },\n backup4: {\n name: '备用线路4',\n icon: 'bolt',\n baseUrl: 'http:\/\/v4.gyks.cf',\n downloadPath: '\/sy\/download\/%E5%AE%89%E5%8D%93%E9%98%85%E8%AF%BBapp-%E6%99%B4%E5%A4%A9%E8%9E%8D%E5%90%884.0(vip%E5%AE%8C%E5%85%A8%E7%89%88).json'\n },\n backup5: {\n name: '备用线路5',\n icon: 'globe',\n baseUrl: 'http:\/\/v5.gyks.cf',\n downloadPath: '\/sy\/download\/%E5%AE%89%E5%8D%93%E9%98%85%E8%AF%BBapp-%E6%99%B4%E5%A4%A9%E8%9E%8D%E5%90%884.0(vip%E5%AE%8C%E5%85%A8%E7%89%88).json'\n },\n backup6: {\n name: '备用线路6',\n icon: 'broadcast-tower',\n baseUrl: 'http:\/\/101.35.133.34:8888',\n downloadPath: '\/sy\/download\/%E5%AE%89%E5%8D%93%E9%98%85%E8%AF%BBapp-%E6%99%B4%E5%A4%A9%E8%9E%8D%E5%90%884.0(vip%E5%AE%8C%E5%85%A8%E7%89%88).json'\n }\n };\n\n \/\/ 版本比较函数\n function compareVersions(vs) {\n const normalize = (v) => {\n return v.split('.').map(n => {\n const num = parseInt(n, 10);\n return isNaN(num) ? 0 : num;\n });\n };\n\n const parts1 = normalize(localVer);\n const parts2 = normalize(vs);\n const maxLength = Math.max(parts1.length, parts2.length);\n \n for (let i = 0; i < maxLength; i++) {\n const num1 = parts1[i] || 0;\n const num2 = parts2[i] || 0;\n if (num1 > num2) return 1;\n if (num1 < num2) return -1;\n }\n return 0;\n }\n\n async function fetchVersionData() {\n \/\/ 使用统一配置中除主线路外的备用线路进行版本检查\n const serversToCheck = Object.values(serverConfig).filter(s => s.baseUrl.includes('gyks.cf') || s.baseUrl.includes('101.35'));\n \n for (const server of serversToCheck) {\n try {\n const response = await fetch(server.baseUrl + '\/version', { timeout: 2000 });\n if (response.ok) {\n return await response.json();\n }\n } catch (e) {\n console.warn(\\`接口失败:\\${server.baseUrl}\\`, e);\n }\n }\n throw new Error('所有更新接口都请求失败');\n }\n\n function showError(message) {\n loading.innerHTML = \\`\n <div class=\"error-state\">\n <div class=\"error-icon\"><i class=\"fas fa-exclamation-triangle\"><\/i><\/div>\n <div class=\"error-text\">\\${message}<\/div>\n <button class=\"retry-button\" onclick=\"location.reload()\"><i class=\"fas fa-redo\"><\/i> 重试<\/button>\n <\/div>\n \\`;\n }\n\n try {\n const data = await fetchVersionData();\n const cloudVersion = String(data.version3);\n const updateLog = data.update_log || {};\n\n \/\/ 显示版本信息\n currentVersion.textContent = \\`v\\${localVer}\\`;\n latestVersion.textContent = \\`v\\${cloudVersion}\\`;\n\n \/\/ 处理日志\n const logEntries = Object.entries(updateLog);\n if (logEntries.length > 0) {\n \/\/ 显示最新日志\n const [latestDate, latestContent] = logEntries[0];\n latestLogDate.innerHTML = \\`<i class=\"fas fa-calendar-alt\"><\/i> \\${latestDate}\\`;\n latestLogContent.textContent = latestContent;\n latestLogContainer.style.display = 'block';\n\n \/\/ 显示历史日志\n if (logEntries.length > 1) {\n const historyLogs = logEntries.slice(1);\n historyCount.textContent = \\`(\\${historyLogs.length}条)\\`;\n logList.innerHTML = historyLogs.map(([date, content]) => \\`\n <div class=\"history-item\">\n <div class=\"history-date\">\n <i class=\"fas fa-calendar-day\"><\/i>\n <span>\\${date}<\/span>\n <\/div>\n <div class=\"history-text\">\\${content}<\/div>\n <\/div>\n \\`).join('');\n logsContainer.style.display = 'block';\n }\n }\n\n \/\/ 检查更新状态\n const compareResult = compareVersions(cloudVersion);\n \n \/\/ 显示版本指示器\n versionIndicator.style.display = 'flex';\n \n if (compareResult === -1) {\n \/\/ 需要更新\n currentStatus.textContent = '待更新';\n currentStatus.className = 'version-status status-outdated';\n versionIndicator.className = 'version-indicator update-needed';\n versionIndicator.innerHTML = '<i class=\"fas fa-arrow-right\"><\/i>';\n\n \/\/ 使用统一配置生成下载按钮\n buttonGroup.innerHTML = Object.values(serverConfig).map(server => {\n const fullUrl = server.baseUrl + server.downloadPath;\n return \\`\n <a href=\"yuedu:\/\/booksource\/importonline?src=\\${encodeURIComponent(fullUrl)}\" class=\"button\">\n <i class=\"fas fa-\\${server.icon}\"><\/i>\n <span>\\${server.name}<\/span>\n <\/a>\n \\`;\n }).join('');\n buttonGroup.style.display = 'flex';\n } else {\n \/\/ 已是最新版本\n currentStatus.textContent = '最新';\n currentStatus.className = 'version-status status-latest';\n versionIndicator.className = 'version-indicator is-latest';\n versionIndicator.innerHTML = '<i class=\"fas fa-check\"><\/i>';\n \n statusAlert.className = 'status-alert up-to-date';\n statusAlert.innerHTML = '<i class=\"fas fa-check-circle\"><\/i> <div>您已是最新版本<\/div>';\n statusAlert.style.display = 'flex';\n }\n\n \/\/ 显示主容器,隐藏加载\n loading.style.display = 'none';\n container.style.display = 'block';\n\n } catch (err) {\n console.error('版本检查失败:', err);\n showError('<i class=\"fas fa-exclamation-circle\"><\/i> 检查更新失败,请稍后重试<br><small>' + err.message + '<\/small>');\n }\n })();\n <\/script>\n<\/body>\n<\/html>\n`;\n java.startBrowser(`data:text\/html;base64,${java.base64Encode(html)}`, '晴天书源更新');\n}",
"respondTime": 180000,
"ruleBookInfo": {
"author": "$.author",
"canReName": "1",
"coverUrl": "$.thumb_url",
"init": "<js>\nif (String(baseUrl).startsWith(\"data:\")) {\n let res = JSON.parse(java.hexDecodeToString(result));\n let book_id = res.book_id;\n let tab = res.tab;\n let sources = res.sources;\n let url = res.url;\n let html = \"\";\n let proxy = getArguments(source.getVariable(), \"proxy\");\n if (url != \"\" && proxy == \"本地\") {\n if (sources == '69书吧') {\n let ck69 = String(cookie.getCookie(url));\n let headers = {\n \"Cookie\": ck69\n };\n let op = JSON.stringify({\n \"headers\": headers\n });\n html = java.ajax(url + ',' + op);\n } else {\n html = java.ajax(url);\n }\n \/\/java.log(html);\n if (html.includes(\"Just a moment...\") && sources == '69书吧') {\n cookie.removeCookie(url);\n var x = `https:\/\/www.69shuba.com`;\n java.longToast('需要真人验证,请进入任意书籍详情页过验证');\n var s = java.startBrowserAwait(x, \"需要真人验证,请进入任意书籍详情页过验证\").body();\n\n let ck69 = String(cookie.getCookie(url));\n let headers = {\n \"Cookie\": ck69\n };\n let op = JSON.stringify({\n \"headers\": headers\n });\n java.log(op);\n html = java.ajax(url + ',' + op);\n \/\/java.log(html);\n }\n }\n let base_url = getArguments(source.getVariable(), \"server\");\n let op = {\n method: \"POST\",\n body: {\n html: html\n }\n };\n op = JSON.stringify(op);\n let varia = String(book.getVariable('custom'));\n if (varia == 'null') {\n varia = '';\n }\n varia = JSON.stringify({\n 'custom': varia\n });\n \/\/varia = java.base64Encode(varia);\n java.log(`${base_url}\/detail?book_id=${book_id}&source=${sources}&tab=${tab}&variable=${varia},${op}`);\n result = java.ajax(`${base_url}\/detail?book_id=${book_id}&source=${sources}&tab=${tab}&variable=${varia},${op}`);\n}\nresult\n<\/js>$.data",
"intro": "<js>\nlet {\n book_id,\n source: sources,\n tab,\n book_tts,\n tags,\n role,\n last_chapter_title,\n last_chapter_update_time,\n word_number,\n status,\n score,\n abstract,\n copyright_info\n} = result;\nlet proxy = getArguments(source.getVariable(), \"proxy\");\nif (proxy == \"本地\") {\n proxy = \"本地网络\";\n} else {\n proxy = \"服务器网络\";\n}\njava.put(\"book_detail\", JSON.stringify(result));\n\nlet base_url = getArguments(source.getVariable(), \"server\");\nlet key = \"\";\ntry {\n let cookieValue =\n String(cookie.getCookie(base_url)) || String(java.getCookie(base_url));\n key = getKey(cookieValue);\n} catch (e) {\n key = \"\";\n}\n\nif (key == \"\") {\n java.log(\"当前服务器未查询到登录状态,尝试查询其他服务器登录状态...\");\n let cookieValue;\n for (let h of host) {\n try {\n cookieValue = String(cookie.getCookie(h)) || String(java.getCookie(h));\n key = getKey(cookieValue);\n } catch (e) {\n key = \"\";\n }\n if (key) {\n java.log(`已在${h}登录,退出查询,正在转移登录状态到当前服务${base_url}`);\n \/\/java.log(cookieValue)\n removeCookie(h);\n removeCookie(base_url);\n cookie.setCookie(base_url, cookieValue);\n break;\n }\n }\n}\n\nif (book.readConfig == null || book.readConfig.useReplaceRule == null) {\n book.setUseReplaceRule(false);\n}\n\nlet nickname = '账号状态:⚠️ 未登录 | 点击右上角 🔖 登录';\ntry {\n let opcx = {\n method: \"GET\",\n headers: {\n cookie: 'qttoken=' + key + ';'\n },\n };\n opcx = JSON.stringify(opcx);\n let user_info = JSON.parse(java.ajax(base_url + '\/get_avatar,' + opcx));\n if (user_info.code == 0) {\n if (user_info.nickname) {\n nickname = '欢迎回来:' + user_info.nickname\n } else {\n nickname = '欢迎回来:' + user_info.email + \"(请前往用户后台设置用户名)\"\n }\n }\n} catch (e) {\n if (key) {\n nickname = '账号状态:已登录'\n }\n};\n\nlet loginStatus = nickname;\n\nlet lightDivider = \"❇️───────❇️───────❇️\";\nlet heavyDivider = \"‎\\n‎\";\n\nlet isValid = (value) => String(value).length > 1;\n\nlet info = `\n 📡 当前服务:${base_url}\n 🔑 ${loginStatus}\n 🏷 数据来源:${sources}\n 🔄 当前模式:${tab}\n ⚙️ 访问模式:${proxy}\n`;\n\nif (tab == \"听书\") {\n let toneId = getArguments(source.getVariable(), \"tone_id\");\n if (isValid(book_tts)) {\n info += `${lightDivider}\n 🎵 音色配置:${toneId}\n ${book_tts}\n`;\n }\n}\n\nlet basicInfo = \"\";\nlet addBasicInfo = (value, prefix, icon) => {\n if (isValid(value)) basicInfo += ` ${icon} ${prefix} ${value}\\n`;\n};\n\naddBasicInfo(tags, \"书籍分类:\", \"🌈\");\naddBasicInfo(role, \"书籍主角:\", \"👑\");\naddBasicInfo(last_chapter_title, \"最新章节:\", \"📚\");\naddBasicInfo(last_chapter_update_time, \"更新时间:\", \"⏳\");\naddBasicInfo(word_number, \"书籍字数:\", \"📊\");\naddBasicInfo(status, \"书籍状态:\", \"🚩\");\naddBasicInfo(score, \"书籍评分:\", \"⭐\");\n\nif (basicInfo) info += `${lightDivider}\\n${basicInfo}`;\n\nif (isValid(abstract)) {\n let indentedAbstract = abstract\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n info += `${heavyDivider}\n 📖 书籍简介:\n${indentedAbstract}\n`;\n} else {\n info += `${heavyDivider}`;\n}\n\nif (isValid(copyright_info)) {\n info += `${lightDivider}\n © ${copyright_info}\n`;\n} else {\n info += `${lightDivider}`;\n}\n\ninfo += `\n${heavyDivider}\n 💠💠💠 数据更新于 ${new Date().toLocaleString()} 💠💠💠\n`;\nlet jjinfo = getArguments(source.getVariable(), \"info\");\n\nif (jjinfo != \"on\") {\n info = String(info)\n .split(\"\\n\")\n .map((line) => line.replace(\/^ {4}\/, \"\"))\n .join(\"\\n\");\n} else {\n basicInfo = \"\";\n addBasicInfo(last_chapter_title, \"最新章节:\", \"📚\");\n addBasicInfo(last_chapter_update_time, \"更新时间:\", \"⏳\");\n addBasicInfo(word_number, \"书籍字数:\", \"📊\");\n addBasicInfo(status, \"书籍状态:\", \"🚩\");\n addBasicInfo(score, \"书籍评分:\", \"⭐\");\n if (isValid(abstract)) {\n let indentedAbstract = abstract\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n basicInfo += `\n \\n‎\n 📖 书籍简介:\n${indentedAbstract}\n`;\n } else {\n basicInfo += `${heavyDivider}`;\n }\n info = `‎\\n🏷 数据来源:${sources}\\n` + basicInfo;\n}\n<\/js>",
"lastChapter": "{{$.source}} {{$.last_chapter_title}} {{$.last_chapter_update_time}}",
"name": "$.book_name",
"tocUrl": "<js>\nlet book_id = result.book_id;\nlet sources = result.source;\nlet tab = result.tab || \"小说\";\nlet url = result.toc_url || \"\";\nvar sdtoken;\ntry {\n var loginInfoMap = source.getLoginInfoMap ? source.getLoginInfoMap() : {};\n sdtoken = String(loginInfoMap['手动填写番茄token(可不填)'] || '');\n} catch (e) {\n sdtoken = '';\n}\nvar rawCookie = getFanqieCookie() || sdtoken;\nvar match = rawCookie.match(\/sessionid=[^;]+\/);\nvar fqcookie = match ? match[0] : '';\nvar fqssionid = '';\nif (fqcookie) {\n fqssionid = getSessionId(fqcookie);\n};\nsetArguments('fqssionid', fqssionid);\njava.put(\"tab\", tab);\njava.put(\"book_id\", book_id);\nlet qtcatalog = {\n book_id: book_id,\n sources: sources,\n tab: tab,\n url: url,\n};\nqtcatalog = java.base64Encode(JSON.stringify(qtcatalog));\n`data:;base64,${qtcatalog},{\"type\":\"qingtian2\"}`;\n<\/js>",
"wordCount": "$.word_number"
},
"ruleContent": {
"content": "<js>\nresult = String(java.hexDecodeToString(result));\nlet res;\nif (result.match(\/晴天融合\/)) {\n result = result.split(\"晴天融合4\");\n res = {\n item_id: result[0],\n tab: result[1],\n title: result[2],\n sources: result[3],\n url: \"\"\n };\n} else {\n res = JSON.parse(result);\n}\nlet varia = String(book.getVariable('custom'));\nif (varia == 'null') {\n varia = '';\n}\nvaria = JSON.stringify({\n 'custom': varia\n});\n\/\/ varia = java.base64Encode(varia);\nlet book_id = res.book_id;\nlet item_id = res.item_id;\nlet tab = res.tab;\nlet title = res.title;\nlet sources = res.sources;\nlet url = res.url;\nlet html = \"\";\nlet proxy = getArguments(source.getVariable(), \"proxy\");\nif (url != \"\" && proxy == \"本地\") {\n if (sources == '69书吧') {\n let ck69 = String(cookie.getCookie(url));\n let headers = {\n \"Cookie\": ck69,\n \"User-Agent\": \"Mozilla\/5.0 (Linux; Android 6.0; Nexus 5 Build\/MRA58N) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/138.0.0.0 Mobile Safari\/537.36 Edg\/138.0.0.0\"\n };\n let op = JSON.stringify({\n \"headers\": headers\n });\n java.log(url);\n java.log(op);\n html = java.ajax(url + ',' + op);\n } else {\n html = java.ajax(url);\n }\n java.log(html);\n if (html.includes(\"Just a moment...\") && sources == '69书吧' && book.durChapterIndex === chapter.index) {\n cookie.removeCookie(url);\n var x = url;\n java.longToast('需要真人验证,请进入任意书籍详情页过验证');\n var s = java.startBrowserAwait(x, \"需要真人验证,请进入任意书籍详情页过验证\").body();\n\n let ck69 = String(cookie.getCookie(url));\n let headers = {\n \"User-Agent\": \"Mozilla\/5.0 (Linux; Android 6.0; Nexus 5 Build\/MRA58N) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/138.0.0.0 Mobile Safari\/537.36 Edg\/138.0.0.0\",\n \"Cookie\": ck69\n };\n let op = JSON.stringify({\n \"headers\": headers\n });\n java.log(op);\n html = java.ajax(url + ',' + op);\n \/\/java.log(html);\n }\n};\nlet content = \"\";\nlet data;\nlet tone_id = getArguments(source.getVariable(), \"tone_id\");\nlet base_url = getArguments(source.getVariable(), \"server\");\nlet device, device_type;\ntry {\n device = java.androidId();\n device_type = \"安卓\";\n} catch (e) {\n try {\n device = java.deviceID();\n device_type = \"苹果\";\n } catch (e) {\n device = \"\";\n device_type = \"安卓\";\n }\n}\n\nlet qtcookie = cookie.getCookie(base_url);\ntry {\n qtcookie = `qttoken=${String(cookie.getKey(base_url, \"qttoken\"))}; deviceId=${device};`\n} catch (e) {}\n\nvar params = {\n html: html,\n item_id: item_id,\n source: sources,\n tab: tab,\n tone_id: tone_id,\n variable: varia,\n version: '4.11.5.1'\n};\n\nvar content_url = '\/content';\n\nif ((sources == \"番茄\" || sources == \"七猫\") && getArguments(source.getVariable(), \"fqpara\") == \"on\" && tab == \"小说\") {\n content_url = '\/content?review=1';\n}\n\n\/\/var signInfo = generateComplexSignature('POST', content_url, {}, APP_SECRET, params);\n\n\n\/\/var signedParams = copyObject(params);\n\/\/signedParams.timestamp = signInfo.timestamp;\n\/\/signedParams.nonce = signInfo.nonce;\n\/\/signedParams.sign = signInfo.sign;\n\n\nvar op = {\n method: \"POST\",\n body: JSON.stringify(params),\n headers: {\n cookie: qtcookie,\n 'Content-Type': 'application\/json'\n }\n};\n\nop = JSON.stringify(op);\njava.log(op);\ndata = java.ajax(base_url + content_url + `,${op}`);\n\ntry {\n data = JSON.parse(data);\n if (data.msg) {\n java.toast(data.msg);\n }\n} catch (e) {}\n\ncontent = data.content\n\nif ((sources == \"番茄\" || sources == \"七猫\") && getArguments(source.getVariable(), \"fqpara\") == \"on\" && tab == \"小说\") {\n var fqssionid = getArguments(source.getVariable(), \"fqssionid\") || \"\";\n content = content\n .replace(\/ident=\"\/g, 'ident=\"' + base_url)\n .replace(\/book_id=\/g, 'book_id=' + book_id + '&ssionid=' + fqssionid);\n if (device_type == \"苹果\") {\n content = paraForiOS(content, sources);\n } else {\n content = paraForAndroid(content, sources);\n }\n\n\n}\ndata = JSON.stringify({\n content: content,\n});\n\nif (device_type == \"安卓\" && (tab == \"短剧\" || tab == \"视频\" || sources == '毒舌影视')) {\n data = {\n content: `【右上角刷新】开启播放(下一集请切换下一章刷新)\\n播放直链:\\n${content}`,\n };\n data = JSON.stringify(data);\n if (book.durChapterIndex === chapter.index) {\n let video_url = `${base_url}\/online_video?book_id=${book_id}&source=${sources}&tab=${tab}`;\n if (sources == '毒舌影视') {\n video_url = content;\n };\n java.startBrowser(video_url, title);\n java.toast(\"正在加载视频...\");\n }\n\n}\ndata;\n<\/js>$.content",
"imageStyle": "TEXT"
},
"ruleExplore": {
"author": "$.author",
"bookList": "$.data",
"bookUrl": "<js>\nlet book_id = result.book_id;\nlet sources = result.source;\nlet tab = result.tab || '小说';\nlet url = result.toc_url || '';\n\nlet qtdetail = {\n book_id: book_id,\n sources: sources,\n tab: tab,\n url: url\n}\nqtdetail = java.base64Encode(JSON.stringify(qtdetail));\n`data:;base64,${qtdetail},{\"type\":\"qingtian\"}`\n<\/js>",
"coverUrl": "$.thumb_url",
"intro": "$.abstract",
"kind": "{{$.category}}\n{{$.score}}\n{{$.status}}\n{{$.source}}\n{{$.tags}}",
"lastChapter": "{{$.last_chapter_title}} • {{$.last_update_time}}",
"name": "$.book_name",
"wordCount": "$.word_number"
},
"ruleSearch": {
"author": "$.author",
"bookList": "$.data",
"bookUrl": "<js>\nlet book_id = result.book_id;\nlet sources = result.source;\nlet tab = result.tab || '小说';\nlet url = result.toc_url || '';\n\nlet qtdetail = {\n book_id: book_id,\n sources: sources,\n tab: tab,\n url: url\n}\nqtdetail = java.base64Encode(JSON.stringify(qtdetail));\n`data:;base64,${qtdetail},{\"type\":\"qingtian\"}`\n<\/js>",
"checkKeyWord": "我的26岁女房客@番茄",
"coverUrl": "$.thumb_url",
"intro": "$.abstract",
"kind": "{{$.status}},{{$.score}},{{$.tags}},{{$.last_chapter_update_time}}",
"lastChapter": "{{$.source}} {{$.last_chapter_title}}",
"name": "$.book_name##(别名:.*?)",
"wordCount": "$.word_number"
},
"ruleToc": {
"chapterList": "<js>\nlet res = JSON.parse(java.hexDecodeToString(result));\nif (res.method) {\n res = Object.fromEntries(\n res.body\n .replace(\"source\", \"sources\")\n .split(\"&\")\n .map((query) => query.split(\"=\"))\n );\n res.url = \"\";\n}\nlet book_id = res.book_id;\njava.put('book_id', book_id);\nlet tab = res.tab;\nlet sources = res.sources;\nlet url = res.url;\nlet html = \"\";\nlet proxy = getArguments(source.getVariable(), \"proxy\");\nif (url != \"\" && proxy == \"本地\") {\n if (sources == '69书吧') {\n let ck69 = String(cookie.getCookie(url));\n let headers = {\n \"Cookie\": ck69\n };\n let op = JSON.stringify({\n \"headers\": headers\n });\n html = java.ajax(url + ',' + op);\n } else {\n html = java.ajax(url);\n }\n \/\/java.log(html);\n if (html.includes(\"Just a moment...\") && sources == '69书吧') {\n cookie.removeCookie(url);\n var x = `https:\/\/www.69shuba.com`;\n java.longToast('需要真人验证,请进入任意书籍详情页过验证');\n var s = java.startBrowserAwait(x, \"需要真人验证,请进入任意书籍详情页过验证\").body();\n\n let ck69 = String(cookie.getCookie(url));\n let headers = {\n \"Cookie\": ck69\n };\n let op = JSON.stringify({\n \"headers\": headers\n });\n \/\/java.log(op);\n html = java.ajax(url + ',' + op);\n \/\/java.log(html);\n }\n};\nlet base_url = getArguments(source.getVariable(), \"server\");\nlet op = {\n method: \"POST\",\n body: {\n html: html\n }\n};\nop = JSON.stringify(op);\nlet varia = String(book.getVariable('custom'));\nif (varia == 'null') {\n varia = '';\n}\nvaria = JSON.stringify({\n 'custom': varia\n});\n\/\/ varia = java.base64Encode(varia);\n\/\/java.log(`${base_url}\/catalog?book_id=${book_id}&source=${sources}&tab=${tab}&variable=${varia},${op}`);\nlet data = java.ajax(\n `${base_url}\/catalog?book_id=${book_id}&source=${sources}&tab=${tab}&variable=${varia},${op}`\n);\nlet device;\nlet device_type;\ntry {\n device = java.androidId();\n device_type = \"安卓\";\n} catch (e) {\n device_type = \"苹果\";\n}\n\nif (tab == \"小说\") {\n if (device_type == \"安卓\") {\n book.type = 8;\n } else {\n book.type = 0;\n }\n} else if (tab == \"听书\") {\n if (device_type == \"安卓\") {\n book.type = 32;\n } else {\n book.type = 1;\n }\n} else if (tab == \"漫画\") {\n if (device_type == \"安卓\") {\n book.type = 64;\n } else {\n book.type = 2;\n }\n} else if (tab == \"短剧\") {\n if (device_type == \"安卓\") {\n book.type = 8;\n } else {\n book.type = 3;\n }\n} else {\n if (device_type == \"安卓\") {\n book.type = 8;\n } else {\n book.type = 0;\n }\n}\ndata;\n<\/js>$.data",
"chapterName": "$.title",
"chapterUrl": "<js>\nlet tab = result.tab;\nlet sources = result.source;\nlet title = result.title;\nlet item_id = result.item_id;\nlet book_id = java.get(\"book_id\");\nlet url = result.toc_url || \"\";\nlet qtcontent = {\n book_id: book_id,\n item_id: item_id,\n title: title,\n sources: sources,\n tab: tab,\n url: url,\n};\nqtcontent = java.base64Encode(JSON.stringify(qtcontent));\nif (sources == '卷') {\n content_url = item_id\n} else if ((sources == \"番茄\" || sources == \"七猫\") && tab == \"小说\") {\n var base_url = getArguments(source.getVariable(), \"server\") || \"\";\n var fqssionid = getArguments(source.getVariable(), \"fqssionid\") || \"\";\n let sourcess = sources.replace('svip_', '');\n content_url = `data:;base64,${qtcontent},{\"type\":\"qingtian3\",\"js\":\"book ? result : '${base_url}\/get_review?book_id=${book_id}&item_id=${item_id}&ssionid=${fqssionid}&source=${sourcess}'\"}`;\n} else {\n content_url = `data:;base64,${qtcontent},{\"type\":\"qingtian3\"}`;\n}\n<\/js>",
"updateTime": "$.first_pass_time"
},
"searchUrl": "<js>\nlet base_url = getArguments(source.getVariable(), 'server');\nlet media;\nlet sources = getArguments(source.getVariable(), 'source');\nif (String(key).startsWith(\"m:\") || String(key).startsWith(\"m:\")) {\n media = \"漫画\"\n key = key.slice(2)\n} else if (String(key).startsWith(\"t:\") || String(key).startsWith(\"t:\")) {\n media = \"听书\"\n key = key.slice(2)\n} else if (String(key).startsWith(\"d:\") || String(key).startsWith(\"d:\")) {\n media = \"短剧\"\n key = key.slice(2)\n} else if (String(key).startsWith(\"x:\") || String(key).startsWith(\"x:\")) {\n media = \"小说\"\n key = key.slice(2)\n} else {\n media = getArguments(source.getVariable(), 'media');\n}\nif (key.includes('@')) {\n var parts = key.split('@');\n key = parts[0];\n sources = parts[1] || sources;\n}\nlet qtcookie = cookie.getCookie(base_url);\nlet op = {\n method: \"GET\",\n headers: {\n cookie: qtcookie\n },\n};\nop = JSON.stringify(op);\n`${base_url}\/search?title=${key}&tab=${media}&source=${sources}&page={{page}},${op}`\n<\/js>",
"weight": 0
}