🌞晴天融合4.7.10(完全版)
晴天融合4.0
晴天 (8653)2天前
4.7.10
优化登录,兼容低版本阅读,感谢@河鹅和.喵~的支持
4.7.4
修复69书吧不可用的问题
新增本地访问,如果发现正文无数据,目录加载不出来,可以点击登录,切换到本地网络访问,但69书吧需要自行开代理访问
4.7.1
新增:幻梦轻小说、QQ浏览器免费小说、得奇小说
大幅优化番茄报错问题(完全版书源)
4.6.30
修复bug
4.6.29
兼容部分用户登录显示未登录的bug
4.6.28
版本大更新,支持番茄书评、章评、段评,重新设计短剧播放页,重构项目,要求强制更新!
3.6.12
目前可用平台
番茄、七猫、得间、QQ、熊猫、69书吧、伪69、喜马拉雅、小米、猫眼、百度、米读、歪瑞古德、顶点、星星小说、笔趣阁22、爱下电子书、搜书神器、知乎
SVIP专属:
书旗、纵横中文网、选书小说、鬼吹灯、万相书城、365小说、八一中文网、全本小说、轻之文库、神书网、影视学、爱笔楼、速读谷、读全本、斋书苑、4020、天域、铅笔轻小说、轻小说文库、中华典藏、福利、绅士漫画、六月听书、海洋听书、九妖漫画、毒舌影视、包子漫画、侠客中文网、晋江文学
如果发现有不好用的,我删除,如果有推荐的,请发送书源给我
{ "bookSourceComment": "更新日志请点击登录,更新书源中查看\n\n兼容正式版阅读app", "bookSourceGroup": "晴天聚合", "bookSourceName": "🌞晴天融合4.7.10(完全版)", "bookSourceType": 0, "bookSourceUrl": "晴天融合4.0", "customOrder": 9, "enabled": true, "enabledCookieJar": true, "enabledExplore": true, "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\nfunction getFanqieCookie() {\n try {\n return String(cookie.getCookie('fanqienovel.com') || java.getCookie('fanqienovel.com') || '');\n } catch (e) {\n return '';\n }\n}\n\nfunction getSessionId(cookieString) {\n const match = cookieString.match(\/sessionid=([^;]+)\/);\n return match ? match[1] : null;\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;\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(\"分组获取失败: \" + e.message);\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 infoData = [{\n title: userName + \"个人中心\",\n url: fqsjurl,\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 1\n }\n }];\n\n groupQuery();\n } catch (e) {\n java.longToast(\"操作失败: \" + e.message);\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>", "jsLib": "var host = [\n'https:\/\/api.qingtian618.com'\n];\n\nfunction getArguments(open_argument, key) {\n try {\n \t 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) {return open_argument[key];} else {return open_argument}\n}\n\nfunction decrypt(Text) {\n return Text;\n}\n\nfunction getComments(content, bid, cid) {\n const { java, cache,source } = this;\n try {\n const base_url = getArguments(source.getVariable(),'server');\n const apiUrl = `${base_url}\/para_idea?item_id=${cid}`;\n const comments = java.ajax(apiUrl);\n \n const lines = content.split(\"\\n\");\n const raw = JSON.parse(comments).data.data;\n\n Object.keys(raw).forEach((x) => {\n cache.putMemory(`fq-${bid}-${cid}-${x}-text`, lines[x]);\n let plcolor = getArguments(source.getVariable(),'plcolor');\n if (!plcolor) {\n \tplcolor = '#000000';\n \t};\n lines[x] += `<img src=\"${this.createSvg(raw[x].count, plcolor, bid, cid, x)}\">`;\n });\n \n return lines.join(\"\\n\");\n } catch (e) {\n return content;\n }\n}\n\nfunction createSvg(number, color, bid, cid, para) {\n const displayText = number > 99 ? \"99+\" : number;\n const date = String(Date.now()).match(\/(\\d{6}$)\/)[1];\n \n const svg = `<svg width=\"1000\" height=\"800\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n <rect x=\"25\" y=\"25\" width=\"950\" height=\"750\" rx=\"250\" ry=\"250\" fill=\"none\" stroke=\"${color}\" stroke-width=\"25\"\/>\n <text x=\"510\" y=\"650\" font-family=\"Arial, sans-serif\" text-anchor=\"middle\" dominant-baseline=\"middle\" font-size=\"30\" fill=\"#000000\">刷新图片查看段评<\/text>\n <text x=\"510\" y=\"565\" font-family=\"Arial, sans-serif\" text-anchor=\"middle\" dominant-baseline=\"middle\" font-size=\"425\" fill=\"${color}\">${displayText}<\/text>\n <\/svg>`;\n \n const encodedSvg = this.java.base64Encode(svg);\n return `data:image\/svg+xml;base64,${encodedSvg},{'js':'showCmt(\"${bid}\",\"${cid}\",\"${para}\",\"${date}\")'}`;\n}\n\nfunction showCmt(bid, cid, para, date) {\n const { java, cache,source } = this;\n const mname = `fq-${bid}-${cid}-${para}`;\n const load = (cache.getFromMemory(mname) || \"-\").split(\"-\");\n \n if (load[0] != \"1\" || load[1] != date) {\n cache.putMemory(mname, \"1-\" + date);\n return;\n }\n \n const base_url = getArguments(source.getVariable(),'server');\n const apiUrl = `${base_url}\/get_para_review?item_id=${cid}&book_id=${bid}¶=${para}`;\n const title = cache.getFromMemory(mname + \"-text\") || \"段评内容\";\n \n java.startBrowser(apiUrl, title);\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\tPackages.android.util.Base64,\n\tPackages.java.lang.String,\n\tPackages.java.net.URL,\n\tPackages.okhttp3.internal.publicsuffix.PublicSuffixDatabase,\n\tPackages.okhttp3.HttpUrl\n);\nwith(javaImport) {\n\tfunction btoa(data) {\n\t\treturn Base64.encodeToString(String(data).getBytes(\"UTF-8\"), 2)\n\t}\n\n\tfunction getSubDomain(url) {\n\t\tlet baseUrl = getBaseUrl(url)\n\t\tif (!baseUrl) {\n\t\t\treturn url\n\t\t}\n\t\ttry {\n\t\t\tlet mURL = URL(baseUrl)\n\t\t\tlet host = mURL.host\n\t\t\tif (isIPAddress(host)) return host\n\t\t\treturn HttpUrl.parse(baseUrl).topPrivateDomain() || host\n\t\t} catch (e) {\n\t\t\tthis.java.log(e)\n\t\t\treturn baseUrl\n\t\t}\n\t}\n\n\tfunction getDomain(url) {\n\t\tlet baseUrl = getBaseUrl(url)\n\t\tif (!baseUrl) {\n\t\t\treturn url\n\t\t}\n\t\ttry {\n\t\t\treturn URL(baseUrl).host\n\t\t} catch (e) {\n\t\t\treturn baseUrl\n\t\t}\n\t}\n\t\t\/**\n\t * 移除cookie\n\t*\/\n\tfunction removeCookie(url) {\n\t\tconst { cookie } = this;\n\t\tcookie.removeCookie(url)\n\t\tlet domains = [getDomain(url), getSubDomain(url)]\n\t\tdomains.forEach(domain => {\n\t\t\tlet cookieGlob = cookie.getCookie(domain)\n\t\t\tif (cookieGlob == null) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcookieGlob.split(\";\").forEach(c => {\n\t\t\t\tlet cookieName = c.split(\"=\")[0]\n\t\t\t\tcookie.setCookie(domain, `${cookieName}=; Expires=Wed, 31 Dec 2000 23:59:59 GMT`)\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunction getBaseUrl(url) {\n\tif (!url) {\n\t\treturn null\n\t}\n\turl = String(url)\n\tif (url.match(\/https?:\\\/\\\/\/i)) {\n\t\tvar index = url.indexOf(\"\/\", 9)\n\t\treturn index == -1 ? url : url.substring(0, index)\n\t}\n\treturn null\n}\n\nfunction isIPv4Address(ip) {\n\tip = String(ip)\n\tlet parts = ip.split('.');\n\tif (parts.length !== 4) return false;\n\n\tfor (let part of parts) {\n\t\tif (!\/^\\d+$\/.test(part)) return false; \/\/ 必须是数字\n\t\tif (part.length > 1 && part[0] === '0') return false; \/\/ 禁止前导零\n\t\tlet num = parseInt(part, 10);\n\t\tif (num < 0 || num > 255) return false; \/\/ 范围检查\n\t}\n\treturn true;\n}\n\nfunction isIPv6Address(ip) {\n\tip = String(ip)\n\t\/\/ 处理双冒号(最多出现一次)\n\tif (ip.includes(':::')) return false;\n\tlet doubleColonCount = (ip.match(\/::\/g) || []).length;\n\tif (doubleColonCount > 1) return false;\n\n\t\/\/ 分割成组\n\tlet groups = ip.split(':');\n\tlet validGroupCount = 8;\n\tlet actualGroupCount = groups.filter(g => g !== '').length;\n\n\t\/\/ 验证组数\n\tif (doubleColonCount === 1) {\n\t\tif (actualGroupCount > validGroupCount - 1) return false;\n\t} else {\n\t\tif (groups.length !== validGroupCount) return false;\n\t}\n\n\t\/\/ 验证每组内容\n\tfor (let group of groups) {\n\t\tif (group === '') continue; \/\/ 跳过空组(双冒号部分)\n\t\tif (!\/^[0-9a-fA-F]{1,4}$\/.test(group)) return false; \/\/ 1-4位十六进制\n\t}\n\treturn true;\n}\n\nfunction isIPAddress(input) {\n\treturn isIPv4Address(input) || isIPv6Address(input)\n}", "lastUpdateTime": "1752094147106", "loginUi": "\n[{\"name\":\"邮箱\",\"type\":\"text\"},\n{\"name\":\"密码\",\"type\":\"password\"},\n{\n \"name\": \"♥UI登录书源\",\n \"type\": \"button\",\n \"action\": \"login(true)\",\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\": \"renderVersionPage()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\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 \"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\": \"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\": \"api()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n}, {\n \"name\": \"🎚切换服务器\",\n \"type\": \"button\",\n \"action\": \"set_server(server)\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n}, {\n \"name\": \"🖱当前服务器 \",\n \"type\": \"button\",\n \"action\": \"get_server()\",\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\": 1\n }\n}, {\n \"name\": \"听书Ai音色填写后点击右上角✔\",\n \"type\": \"text\"\n}, {\n \"name\": \"自定义服务器(可不填)\",\n \"type\": \"text\"\n}, {\n \"name\": \"自定义评论颜色(可不填)\",\n \"type\": \"text\"\n}, {\n \"name\": \"手动填写番茄token(可不填)\",\n \"type\": \"text\"\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 '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('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('伪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('万相书城')\",\n 'name': '万相书城',\n 'type': 'button',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n}, {\n 'action': \"set_source('365小说')\",\n 'name': '365小说',\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 'action': \"set_source('4020')\",\n 'name': '4020',\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, {\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('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 '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 ", "loginUrl": "\/\/ 当前书源版本号,切勿修改,否则影响更新的识别\nconst localVersion = '4.7.10';\n\n\nfunction login(flag) {\n\tif (flag == undefined) {\n\t\tresult = JSON.parse(source.getLoginInfo())\n\t} else {\n\t\tputLoginInfo(JSON.stringify(result))\n\t}\n\tlet base_url = getArgument('server')\n\tlet zdyserver = String(result['自定义服务器(可不填)']);\n\tif (zdyserver.includes('http')) {\n\t\tsetArgument('server', zdyserver);\n\t\tif (cookie.getKey(base_url,\"qttoken\")){\n\t\t\tlet cookies=cookie.getCookie(base_url)\n\t\t\tremoveCookie(base_url)\n\t\t\tcookie.setCookie(zdyserver,cookies)\n\t\t}\n\t\tjava.toast(`\\n\\n当前服务器为自定义服务器\\n${zdyserver}\\n\\n切换服务器请先清空服务器地址中的数据`);\n\t}\n\tlet zdytone_id = String(result['听书Ai音色填写后点击右上角✔'] || '');\n\tif (zdytone_id) {\n\t\tsetArgument('tone_id', zdytone_id);\n\t} else {\n\t\tsetArgument('tone_id', '默认音色');\n\t}\nlet plcolor = String(result['自定义评论颜色(可不填)'] || '#000000');\n\tif (plcolor) {\n\t\tsetArgument('plcolor', plcolor);\n\t} else {\n\t\tsetArgument('plcolor', '#000000');\n\t}\n\n\tlet sq_user_id = String(result['书旗id(可不填)'] || '');\n\tif (sq_user_id) {\n\t\tsetArgument('sq_user_id', sq_user_id);\n\t} else {\n\t\tsetArgument('sq_user_id', '0');\n\t}\n\tbase_url = getArgument('server')\n\tlet register_email = String(result['邮箱'])\n\tlet password = String(result['密码'])\n\tlet key = String(result['密钥'])\n\t\n\t\/\/java.log(cookie.getCookie(base_url))\n\tif ((register_email && password || key)&& !String(cookie.getKey(base_url,\"qttoken\"))) {\n\t\tremoveCookie(base_url)\n\t\tlet deviceKey = java.webView('', '', 'navigator.userAgent+window.screen.width+window.screen.height')\n\t\tlet deviceId = java.digestHex(deviceKey, \"SHA256\")\n\t\tjava.longToast(\"正在登录中...\")\n\t\tif (register_email && password){\n\t\t\tlet options = JSON.stringify({\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: {\n\t\t\t\t\t'Content-Type': 'application\/json'\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tregister_email: result['邮箱'],\n\t\t\t\t\tpassword: result['密码']\n\t\t\t\t})\n\t\t\t})\n\t\t\ttry {\n\t\t\t\tlet data = JSON.parse(java.ajax(`${base_url}\/login_api,${options}`))\n\t\t\t\tif (data.code == 0) {\n\t\t\t\t\tjava.toast(\"登录成功\")\n\t\t\t\t\tcookie.setCookie(base_url, `qttoken=${data.key};deviceId=${deviceId}`)\n\t\t\t\t\tresult['密钥']=data.key\n\t\t\t\t\tputLoginInfo(JSON.stringify(result))\n\t\t\t\t} else {\n\t\t\t\t\tjava.toast(data.msg || \"登录失败,请重试!\")\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tjava.toast(\"登录失败,请重试!\\n\" + e.message)\n\t\t\t}\n\t\t} else {\n\t\t\tcookie.setCookie(base_url, `qttoken=${key};deviceId=${deviceId}`)\n\t\t\tlet res=java.ajax(`${base_url}\/user_api,{\"method\":\"POST\"}`)\n\t\t\ttry {\n\t\t\t\tres=JSON.parse(res)\n\t\t\t\tif (res.id!=undefined) {\n\t\t\t\t\tjava.toast('密钥登录成功')\n\t\t\t\t\tresult['邮箱'] = res.email\n\t\t\t\t\tputLoginInfo(JSON.stringify(result))\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error()\n\t\t\t\t}\n\t\t\t} catch(e) {\n\t\t\t\tjava.log(e)\n\t\t\t\tjava.toast(\"登录失败\")\n\t\t\t}\n\t\t}\n\t} else if (flag&&String(cookie.getKey(base_url,\"qttoken\"))) {\n\t\tjava.toast(\"当前已登录,请退出登录后重新登录\");\n\t\t\/\/checkStatus();\n\t}\n}\n\n\nfunction checkStatus() {\n\tlet base_url = getArgument('server')\n\tlet res=java.ajax(`${base_url}\/user_api,{\"method\":\"POST\"}`)\n\ttry {\n\t\t\t\tres=JSON.parse(res)\n\t\t\t\tif (res.id!=undefined) {\n\t\t\t\t\tresult['邮箱'] = res.email\n\t\t\t\t\tputLoginInfo(JSON.stringify(result))\n\t\t\t\t\tlet devices\n\t\t\t\t\ttry {\n\t\t\t\t\t\tdevices = JSON.parse(res.device).length;\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tdevices = res.device ? 1 : 0;\n\t\t\t\t\t}\n\t\t\t\t\tlet isVip;\n\t\t\t\t\tif (res.is_vip==1) {\n\t\t\t\t\t\tisVip = 'VIP';\n\t\t\t\t\t} else if (res.is_vip>=2) {\n\t\t\t\t\t\tisVip = 'SVIP';\n\t\t\t\t\t} else {\n\t\t\t\t\t\tisVip = '普通会员';\n\t\t\t\t\t}\n\t\t\t\t\ttips= `\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\t\t\t\t\tjava.log(tips)\n\t\t\t\t\tjava.longToast(tips)\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(res.msg)\n\t\t\t\t}\n\t\t\t} catch(e) {\n\t\t\t\t\/\/java.log(e)\n\t\t\t\tjava.toast(\"\\n检测登录失败\\n\"+e.message)\n\t\t\t}\n}\n\nfunction clearDevice() {\n\tlet base_url = getArgument('server')\n\tlet res=JSON.parse(java.ajax(`${base_url}\/clear,{\"method\":\"POST\"}`))\n\tjava.toast(res.code === 0 ? \"设备清除成功\" : res.msg)\n\tPackages.java.lang.Thread.sleep(500)\n\tcheckStatus()\n}\n\/\/ 保存登录UI信息\nfunction putLoginInfo(info) {\n\ttry {\n\t\tlet key = java.androidId()\n\t\tlet encodeStr = Packages.android.util.Base64.encodeToString(java.createSymmetricCrypto(\"AES\", key).encrypt(info), 2)\n\t\tcache.put(`userInfo_${source.getKey()}`, encodeStr)\n\t\treturn true\n\t} catch (e) {\n\t\tjava.log(e)\n\t\treturn source.putLoginInfo(info)\n\t}\n}\nfunction qtsj() {\n\tjava.startBrowserAwait(getArgument('server') + '\/online_bookshelf','晴天书架');\n\t}\n\n\/\/ 填写密钥\nfunction loginqt() {\n java.startBrowserAwait(getArgument('server') + '\/login', '登录晴天小说书源');\n }\n\n\/\/登录番茄\nfunction fq_login() {\n try {\n \t java.startBrowserAwait(\"https:\/\/fanqienovel.com\/\", \"登录\")\n } catch (e) {\n java.toast(e)\n }\n try {\n cookie.removeCookie(\"snssdk.com\")\n } catch (e) {\n }\n var cookies = cookie.getCookie(\"fanqienovel.com\")\n if (!cookies || cookies == \"\") {\n java.toast(\"登录失败!\")\n return false\n }\n java.toast(\"登录成功!\")\n return true\n}\n\n\/\/退出登录\nfunction logout() {\n cookie.removeCookie(\"fanqienovel.com\");\n cookie.removeCookie(\"snssdk.com\");\n \n const servers = host\n for (let server of servers) {\n cookie.removeCookie(server);\n }\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\tlet proxy = getArgument('proxy');\n\tif (proxy=='本地') {\n\t\tsetArgument('proxy','云端');\n\t\tjava.longToast('\\n所有数据采用\\n\\n服务器网络访问\\n\\n如果发现用不了,请切换本地网络访问,如69书吧');\n\t\t\t\t} else {\n\t\t\tsetArgument('proxy','本地');\n\t\t\tjava.longToast('\\n所有数据采用\\n\\n本地网络访问\\n\\n如果发现用不了,请开启网络代理,如69书吧');\n\t\t\t\t\t\t}\n\t}\n\n\n\/\/设置男女频\nfunction set_source_type(source_type) {\n\t setArgument(\"source_type\", source_type);\n\t java.toast(\"\\n发现页已设置为:\"+source_type);\n\t}\n\t\n\/\/首页\nfunction api() { java.startBrowserAwait('http:\/\/vip.qingtian618.com', \"首页\");\n}\n\n\/\/打赏\nfunction vip() {\n java.startBrowserAwait(getArgument('server') + '\/coffee', \"喝咖啡\");\n}\n\n\/\/获取密钥\nfunction hqmy() {\n java.startBrowserAwait(getArgument('server') + '\/key', \"获取密钥\");\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 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 java.toast(`\\n\\n当前使用源:${source}-${media}`);\n\n}\n\n\/\/获取服务器\nfunction get_server() {\n var server = getArgument('server');\n var tishi = '\\n\\n当前服务器:' + server\n java.toast(tishi);\n return server\n}\n\n\n\/\/设置服务器\nfunction set_server() {\n let zdyserver;\n try {\n zdyserver = String(source.getLoginInfoMap()['自定义服务器(可不填)']);\n if (zdyserver.includes('http')) {\n setArgument('server', zdyserver);\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 java.toast(`\\n\\n当前服务器${nextIndex+1}:${nextServer}\\n\\n注意:该功能需要打赏后导入vip书源解锁`);\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 let zdytone_id;\n try {\n zdytone_id = String(source.getLoginInfoMap()['其他音色填写后点击右上角✔']);\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(source) {\n\t setArgument('source', source);\n\t set_media('小说');\n java.toast(`\\n\\n当前来源已切换为:\\n${source}\\n\\n切换后请重新搜索`);\n }\n\n\n\/\/ 番茄段评\nfunction paracomment(a) {\n let status = cache.get(a);\n if (status == \"on\") {\n cache.put(a, \"off\");\n java.toast(\"\\n段评已关闭\");\n } else {\n cache.put(a, \"on\");\n java.longToast(\"段评已开启\\n\\n长按刷新段后面的图片即可\\n\\n如果图片不显示,刷新无反应\\n请更新测试版阅读app\");\n }\n}\n\n\/\/ 书源更新\nfunction renderVersionPage() {\n\tlet 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 <style>\n body {\n margin: 0;\n padding: 1em;\n font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n background: linear-gradient(135deg, #e0c3fc 0%, #8ec5fc 100%);\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n color: #333;\n }\n\n .container {\n width: 100%;\n max-width: 480px;\n background: rgba(255, 255, 255, 0.85);\n backdrop-filter: blur(10px);\n border-radius: 16px;\n padding: 1em;\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.1);\n animation: fadeIn 0.5s ease-in-out;\n display: none; \n }\n\n @keyframes fadeIn {\n from { opacity: 0; transform: translateY(10px); }\n to { opacity: 1; transform: translateY(0); }\n }\n\n h1 {\n text-align: center;\n font-size: 1.6em;\n margin-bottom: 1em;\n color: #333;\n }\n\n .version-info, .status {\n text-align: center;\n margin-bottom: 1em;\n font-weight: 500;\n }\n\n .status {\n color: #d63384;\n font-size: 0.9em;\n }\n\n .button-group {\n display: flex;\n flex-direction: column;\n gap: 0.75em;\n margin-bottom: 1.5em;\n }\n\n .button {\n display: block;\n width: 90%;\n padding: 0.85em;\n text-align: center;\n font-size: 1em;\n border: none;\n border-radius: 8px;\n text-decoration: none;\n background: linear-gradient(135deg, #42e695 0%, #3bb2b8 100%);\n color: white;\n font-weight: bold;\n transition: all 0.25s ease;\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.15);\n }\n\n .button:hover {\n transform: translateY(-2px);\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);\n }\n\n .logs {\n background: #ffffffcc;\n padding: 1em;\n border-radius: 8px;\n border: 1px solid #ddd;\n box-shadow: inset 0 1px 3px rgba(0,0,0,0.05);\n }\n\n .logs h2 {\n font-size: 1.2em;\n margin-bottom: 0.8em;\n border-bottom: 1px solid #ccc;\n padding-bottom: 0.3em;\n color: #222;\n }\n\n .log-item {\n margin-bottom: 0.7em;\n line-height: 1.5;\n }\n\n .log-item-date {\n font-weight: bold;\n color: #3b3b3b;\n }\n\n .log-item-content {\n margin-left: 1em;\n color: #555;\n }\n\n .loading {\n text-align: center;\n font-size: 1.1em;\n color: #555;\n animation: pulse 1.2s infinite;\n }\n\n @keyframes pulse {\n 0% { opacity: 1; }\n 50% { opacity: 0.5; }\n 100% { opacity: 1; }\n }\n .ad-banner {\n background: linear-gradient(135deg, #ff9a9e 0%, #fad0c4 100%);\n color: white;\n padding: 10px 15px;\n text-align: center;\n margin-bottom: 20px;\n cursor: pointer;\n border-radius: 8px;\n border: 1px solid #ddd;\n box-shadow: inset 0 1px 3px rgba(0,0,0,0.05);\n }\n\n .ad-banner:hover {\n transform: translateY(-2px);\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);\n }\n\n .ad-banner span {\n font-weight: 500;\n font-size: 16px;\n }\n <\/style>\n<\/head>\n<body>\n <div id=\"loading\" class=\"loading\">🔍 正在拼命检查中,请稍候~ 📡<\/div>\n\n <div class=\"container\" id=\"container\">\n <h1>📚 书源版本检查<\/h1>\n <div class=\"ad-banner\" onclick=\"window.location.href='https:\/\/api.qingtian618.com\/phonecardad'\">\n <span>📱 手机卡充值优惠活动,点击查看详情 →<\/span>\n <\/div>\n <div class=\"version-info\" id=\"versionInfo\">加载中...<\/div>\n <div class=\"status\" id=\"statusText\"><\/div>\n\n <div class=\"button-group\" id=\"buttonGroup\" style=\"display: none;\">\n <\/div>\n\n <div class=\"logs\" id=\"logs\" style=\"display: none;\">\n <h2>📝 更新日志<\/h2>\n <div id=\"logList\"><\/div>\n <\/div>\n <\/div>\n\n <script>\n(async function() {\n const loading = document.getElementById('loading');\n const container = document.getElementById('container');\n const versionInfo = document.getElementById('versionInfo');\n const statusText = document.getElementById('statusText');\n const updateButton = document.getElementById('updateButton');\n const buttonGroup = document.getElementById('buttonGroup');\n const logsContainer = document.getElementById('logs');\n const logList = document.getElementById('logList');\n\nconst localVer = '${String(localVersion)}';\n\/\/ 版本判断逻辑\nfunction compareVersions(vs) {\n const parts1 = localVer.split('.').map(Number);\n const parts2 = vs.split('.').map(Number);\n const maxLength = Math.max(parts1.length, parts2.length);\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\nconst encodedEndpoints = [\n 'aHR0cHM6Ly9hcGkucWluZ3RpYW42MTguY29t', \n 'aHR0cHM6Ly92MS5xaW5ndGlhbjYxOC5jb20=', \n 'aHR0cHM6Ly92Mi5xaW5ndGlhbjYxOC5jb20=', \n 'aHR0cHM6Ly92My5xaW5ndGlhbjYxOC5jb20=', \n 'aHR0cHM6Ly92NC5xaW5ndGlhbjYxOC5jb20=', \n 'aHR0cHM6Ly92NS5xaW5ndGlhbjYxOC5jb20=', \n 'aHR0cDovLzExMy40NS4xNzUuMTEyOjg4ODg=' \n];\n\nfunction decodeEndpoint(str) {\n return atob(str);\n}\n\nasync function fetchVersionData() {\n for (const b64 of encodedEndpoints) {\n const url = decodeEndpoint(b64);\n try {\n const response = await fetch(url + '\/version', { timeout: 2000 }); \n if (response.ok) {\n return await response.json();\n }\n } catch (e) {\n console.warn(\\`接口失败:\\${url}\\`, e);\n }\n }\n throw new Error('所有更新接口都请求失败');\n}\n try {\n loading.style.display = 'block';\n\n const data = await fetchVersionData();\n const cloudVersion = String(data.version3);\n const updateLog = data.update_log || {};\n\n versionInfo.innerHTML = \\`📌 当前版本:v\\${localVer} | 云端版本:v\\${cloudVersion}\\`;\n\n logList.innerHTML = Object.entries(updateLog).map(([date, content]) => \\`\n <div class=\"log-item\">\n <div class=\"log-item-date\">\\${date}<\/div>\n <div class=\"log-item-content\">\\${content}<\/div>\n <\/div>\n \\`).join('');\n logsContainer.style.display = 'block';\n\n if (compareVersions(cloudVersion) === -1) {\n statusText.innerHTML = '<span>✨ 有新版本可用,建议立即更新!<br>${yd}<\/span>';\nconst domainMap = {\n main: 'aHR0cDovL3N5LnFpbmd0aWFuNjE4LmNvbQ==', \n d1: 'aHR0cHM6Ly9hcGkucWluZ3RpYW42MTguY29t', \n d2: 'aHR0cHM6Ly92MS5xaW5ndGlhbjYxOC5jb20=',\n d3: 'aHR0cHM6Ly92Mi5xaW5ndGlhbjYxOC5jb20=',\n d4: 'aHR0cHM6Ly92My5xaW5ndGlhbjYxOC5jb20=',\n d5: 'aHR0cHM6Ly92NC5xaW5ndGlhbjYxOC5jb20=',\n d6: 'aHR0cHM6Ly92NS5xaW5ndGlhbjYxOC5jb20='\n};\n\nfunction decode(b64) {\n return atob(b64);\n}\n\nconst path = '\/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(%E5%AE%8C%E5%85%A8%E7%89%88).json';\nconst mainPath = '\/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(%E5%AE%8C%E5%85%A8%E7%89%88).json';\n\nconst routes = [\n { name: '🚀 主线路', domain: 'main', suffix: mainPath },\n { name: '📦 备用线路1', domain: 'd1', suffix: path },\n { name: '🛰️ 备用线路2', domain: 'd2', suffix: path },\n { name: '🛰️ 备用线路3', domain: 'd3', suffix: path },\n { name: '🛰️ 备用线路4', domain: 'd4', suffix: path },\n { name: '🛰️ 备用线路5', domain: 'd5', suffix: path },\n { name: '🛰️ 备用线路6', domain: 'd6', suffix: path }\n];\n\nbuttonGroup.innerHTML = routes.map(r => {\n const fullUrl = decode(domainMap[r.domain]) + r.suffix;\n return \\`<a href=\"yuedu:\/\/booksource\/importonline?src=\\${encodeURIComponent(fullUrl)}\" class=\"button\">\\${r.name}<\/a>\\`;\n}).join('');\n buttonGroup.style.display = 'flex';\n } else {\n statusText.textContent = '✅ 已是最新书源咯~';\n }\n\n loading.style.display = 'none';\n container.style.display = 'block';\n\n } catch (err) {\n loading.textContent = '😢 检查失败啦~ 请稍后再试==>'+err;\n console.error('版本检查失败:', err);\n }\n})();\n<\/script>\n<\/body>\n<\/html>\n`;\n java.startBrowser(`data:text\/html;base64,${java.base64Encode(html)}`, '晴天书源更新');\n}\n\n\n\n", "respondTime": 180000, "ruleBookInfo": { "author": "$.author", "canReName": "1", "coverUrl": "$.thumb_url", "init": "<js>\nlet res = JSON.parse(java.hexDecodeToString(result));\nlet book_id = res.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\thtml = java.ajax(url);\n\t\/\/java.log(html);\n\t}\nlet base_url = getArguments(source.getVariable(),'server');\nlet op = {\"method\":\"POST\",\"body\":{\"html\":html}};\nop = JSON.stringify(op);\n\/\/java.log(op);\nlet data = java.ajax(`${base_url}\/detail?book_id=${book_id}&source=${sources}&tab=${tab},${op}`\n);\ndata;\n<\/js>\n$.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\tproxy = '本地网络';\n\t} else {\n proxy = '服务器网络';\n\t\t};\njava.put('book_detail', JSON.stringify(result));\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}\nlet base_url = getArguments(source.getVariable(), 'server');\nlet key = '';\ntry {\n let cookieValue = String(cookie.getCookie(base_url)) || String(java.getCookie(base_url));\n key = getKey(cookieValue);\n } catch (e) {\n key = '';\n}\n\nif (key=='') {\n\tjava.log(\"当前服务器未查询到登录状态,尝试查询其他服务器登录状态...\")\n\tlet cookieValue;\n\tfor (let h of host) {\n\t\ttry {\n\t\t\tcookieValue = String(cookie.getCookie(h)) || String(java.getCookie(h));\n\t\t\tkey = getKey(cookieValue);\n\t\t} catch (e) {\n key = '';\n }\n if (key) {\n \tjava.log(`已在${h}登录,退出查询,正在转移登录状态到当前服务${base_url}`)\n \t\/\/java.log(cookieValue)\n \tremoveCookie(h)\n \tremoveCookie(base_url)\n \tcookie.setCookie(base_url,cookieValue)\n \tbreak;\n }\n\t}\n}\n\nif (book.readConfig==null||book.readConfig.useReplaceRule==null) {\n\tbook.setUseReplaceRule(false)\n}\nlet loginStatus = key ? '✅ 已登录' : '⚠️ 未登录 | 点击右上角 🔖 登录';\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\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.split('\\n').map(line => ` ${line}`).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`;\n\ninfo = String(info).split('\\n').map(line => line.replace(\/^ {4}\/, '')).join('\\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 || '';\njava.put('tab',tab);\njava.put('book_id',book_id);\nlet qtcatalog = {\n\tbook_id:book_id,\n\tsources:sources,\n\ttab:tab,\n\turl:url\n\t}\n\tqtcatalog = java.base64Encode(JSON.stringify(qtcatalog));\n`data:;base64,${qtcatalog},{\"type\":\"qingtian2\"}`\n<\/js>", "wordCount": "$.word_number" }, "ruleContent": { "content": "<js>\nlet res = JSON.parse(java.hexDecodeToString(result));\nlet book_id = java.get('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\t\thtml = java.ajax(url);\n\t\t\/\/java.log(html);\n\t}\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}\nlet op = {\"method\":\"POST\",\n\"body\":{html:html},\n\"headers\":{\"cookie\":cookie.getCookie(base_url)}\n};\nop = JSON.stringify(op);\n\/\/java.log(op);\nlet content_url = `\/content?item_id=${item_id}&source=${sources}&tab=${tab}&tone_id=${tone_id}&version=4.6.29,${op}`;\n\ndata = java.ajax(base_url + content_url);\ntry {\n if (JSON.parse(data).msg) {\n java.toast(JSON.parse(data).msg);\n }\n} catch (e) {}\ndata = JSON.parse(data);\ncontent = data.content;\ncontent = decrypt(content);\nif (sources == '番茄' && cache.get(\"fqpara\") == \"on\" && tab == \"小说\") {\n content = getComments(content, book_id, item_id);\n}\ndata = JSON.stringify({\n 'content': content\n});\n\/\/java.log(decrypt(data.content));\nif (device_type == '安卓' && (tab == '短剧' || tab == '视频')) {\n data = {\n 'content': `【右上角刷新】开启播放(下一集请切换下一章刷新)\\n播放直链:\\n${content}`\n }\n data = JSON.stringify(data);\n if (book.durChapterIndex === chapter.index) {\n \t let video_url = `${base_url}\/online_video?book_id=${book_id}&source=${sources}&tab=${tab}`;\n java.startBrowser(video_url, title);\n java.toast('正在加载视频...');\n }\n};\ndata;\n<\/js>\n$.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\tbook_id:book_id,\n\tsources:sources,\n\ttab:tab,\n\turl:url\n\t}\n\tqtdetail = java.base64Encode(JSON.stringify(qtdetail));\n`data:;base64,${qtdetail},{\"type\":\"qingtian\"}`\n<\/js>", "coverUrl": "$.thumb_url", "intro": "{{$.source}}\n{{$.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\tbook_id:book_id,\n\tsources:sources,\n\ttab:tab,\n\turl:url\n\t}\n\tqtdetail = 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));\n\nlet book_id = res.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\thtml = java.ajax(url);\n\t\/\/java.log(html);\n\t}\nlet base_url = getArguments(source.getVariable(),'server');\nlet op = {\"method\":\"POST\",\"body\":{\"html\":html}};\nop = JSON.stringify(op);\n\/\/java.log(op);\nlet data = java.ajax(`${base_url}\/catalog?book_id=${book_id}&source=${sources}&tab=${tab},${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\tif (device_type == '安卓') {\n book.type = 8;} else {\n \tbook.type = 0;\n \t}\n } \n else if (tab=='听书') {\n \tif (device_type == '安卓') {\n book.type = 32;} else {\n \tbook.type = 1;\n \t}\n \t} else if (tab=='漫画') {\n \t\tif (device_type == '安卓') {\n book.type = 64;} else {\n \tbook.type = 2;\n \t}\n \t } else if (tab=='短剧') {\n \t\t\tif (device_type == '安卓') {\n book.type = 8;} else {\n \tbook.type = 3;\n \t}\n \t\t \t\t\t} else {\n \t\t \t\t\t\tif (device_type == '安卓') {\n book.type = 8;} else {\n \tbook.type = 0;\n \t}\t\t\n \t\t \t\t\t\t}\ndata;\n\t<\/js>\n$.data", "chapterName": "$.title", "chapterUrl": "<js>\nlet tab = result.tab;\nlet sources = result.source;\nlet title = result.title;\nlet item_id = result.item_id;\nlet url = result.toc_url || '';\nlet qtcontent = {\n\titem_id:item_id,\n\ttitle:title,\n\tsources:sources,\n\ttab:tab,\n\turl:url\n\t}\n\tqtcontent = java.base64Encode(JSON.stringify(qtcontent));\n\nif (sources == '番茄' && tab == '小说') {\n\tbook_id = java.get('book_id');\n\tjava.put('book_id',book_id);\n\tvar base_url = getArguments(source.getVariable(), 'server') || '';\n content_url= `data:;base64,${qtcontent},{\"type\":\"qingtian3\",\"js\":\"book ? result : '${base_url}\/get_review?book_id=${book_id}&item_id=${item_id}'\"}`;\n\t} else {\n content_url= `data:;base64,${qtcontent},{\"type\":\"qingtian3\"}`;\n\t\t}\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\tmedia = \"小说\"\n key = key.slice(2)\n\t} else {\n\tmedia = getArguments(source.getVariable(),'media');\n\t}\nif (key.includes('@')) {\n var parts = key.split('@');\n key = parts[0];\n sources = parts[1] || sources;\n}\n\t`${base_url}\/search?title=${key}&tab=${media}&source=${sources}&page={{page}}`\n<\/js>", "weight": 60 }