🔅光遇聚合(26.5.31)
光遇聚合
晴天 (8653)5天前
修复已知bug
{
"bookSourceComment": "如果是用的官方版阅读,或者低版本阅读,推荐更新阅读以使用新功能,否则很多功能无法使用哦\n软件下载地址:https:\/\/legado.gyks.cf\n推荐使用阅读R或者阅读Sigma\n\n\n晴天聚合重构,已适配所有安卓版本阅读\n目前不适配苹果端栖阅(可以正常阅读,发现页无法适配)\n\n更新日志请点击登录,更新书源中查看\n\n基础使用教程:\n1、搜索:\n 1)基础搜索:在发现页长按该书源,点击搜索,直接搜,搜索范围(全部来源小说)\n 2)进阶搜索:在登陆-书源设置中选择指定模式\/来源,可搜索指定模式\/来源内容\n 3)快捷搜索:使用特殊关键词(x,t,m,d,@)搜索\n x:书名@来源 如(x:十日终焉@番茄)\n t:书名@来源 如(t:十日终焉@番茄)\n m:书名@来源 如(m:十日终焉@番茄)\n d:书名@来源 如(d:十日终焉@番茄)\n 其中:x表示小说(可省略),t表示听书,m表示漫画,d表示短剧\n 冒号支持中文英文\n \n2、发现页:\n 在书源设置中选择指定模式或者单个来源,然后刷新发现页即可\n \n3、更多使用方式进入书源设置页面应该能一目了然",
"bookSourceGroup": "聚合,番茄,七猫,塔读,QQ阅读,书旗,轻小说",
"bookSourceName": "🔅光遇聚合(26.5.31)",
"bookSourceType": 0,
"bookSourceUrl": "光遇聚合",
"bookUrlPattern": "https?:\\\/\\\/(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z0-9-]+(?::\\d+)?\\\/detail.*",
"customButton": false,
"customOrder": 4,
"enabled": true,
"enabledCookieJar": true,
"enabledExplore": true,
"eventListener": false,
"exploreUrl": "<js>\nlet moreSettings = getVariable('更多设置');\nvar base_url = BaseUrl();\nvar source_type = getVariable('频道') || '男频';\nlet tab = moreSettings && moreSettings['搜索模式'] || '小说';\nlet sources = getVariable(\"发现页来源\") || moreSettings && moreSettings[tab] || '全部';\nlet islyc = checkEnv();\n\nif (islyc != \"苹果\" && islyc != \"安卓\"){\n\ttab = getVariable(\"发现页类型\");\n\t}\n\n\nlet js;\n\njs = getVariable('云端配置');\n\/\/java.longToast(JSON.stringify(js))\nif (!!!js) {\n getCloudSettings(true);\n js = getVariable('云端配置');\n }\n\n\nlet source_list = [];\ntry{\nsource_list = js[tab];\n} catch (e) {\n\tjava.longToast(e);\n\t}\n\nlet fqssionid = getFqToken();\nif (!fqssionid && (sources == '番茄' || sources == '全部')) {\n java.toast('您还未登陆番茄账号,无法同步数据哦!');\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\n\nvar groupDatas = [];\nvar infoData = [ ];\nif (sources == '番茄' || sources == '全部') {\ninfoData = [{\n \"title\": \"登录番茄\",\n \"url\": '{\\{java.startBrowser(\"https:\/\/fanqienovel.com\/\",\"番茄登录\");}}',\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.45\n }\n }];\n}\n\nvar hasValidCookie = fqssionid.length > 0;\n\nif (hasValidCookie && (sources == '番茄' || sources == '全部')) {\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.25\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.25\n }\n });\n }\n if (groupDatas.length % 3 != 0) {\n groupDatas.push({\n title: \"--\",\n url: \"\",\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 0.25\n }\n });\n }\n } catch (e) {\n java.longToast(\"番茄登录过期,已隐藏番茄书架\" + fqssionid);\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: '',\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 1\n }\n },\n {\n title: '番茄书架',\n url: fqsjurl,\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 0.25\n }\n }, \n {\n title: \"个性推荐\",\n url: fqtjurl,\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 0.25\n }\n }, {\n title: \"历史阅读\",\n url: fqlsurl,\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 0.25\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}\nlet qttoken = getToken();\n\/\/let qtcookie = \"\"\nlet qtop = {\n method: \"GET\",\n headers: {\n cookie: `qttoken=${qttoken}`\n },\n};\n\n\n\nqtop = JSON.stringify(qtop);\nlet qtsjurl = base_url + '\/get_book_shelf,' + qtop;\n\nlet qtsj = [];\n\nif (islyc != \"苹果\" && islyc != \"安卓\") {\n try {\n let hostsbk = getVariable('云端配置')['hosts'] || hosts;\n qtsj.push(createFilter(\n \"线路\",\n hostsbk,\n base_url,\n \"线路\",\n 1,\n \"'线路'\"\n ));\n qtsj.push(createFilter(\n \"类型\",\n [\"小说\", \"听书\", \"短剧\", \"漫画\"],\n tab,\n \"发现页类型\",\n 0.33,\n \"'类型'\"\n ));\n qtsj.push(createFilter(\n \"频道\",\n [\"男频\", \"女频\"],\n source_type,\n \"频道\",\n 0.33,\n \"'频道'\"\n ));\n qtsj.push(createFilter(\n \"平台\",\n source_list,\n sources,\n \"发现页来源\",\n 1,\n \"'平台'\"\n ));\n \n qtsj.push({\n \"title\": \"更新配置\",\n \"type\": \"button\",\n \"action\": \"getCloudSettings(true)\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n })\n qtsj.push({\n \"title\": \"更新书源\",\n \"type\": \"button\",\n \"action\": \"renderVersionPage()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n })\n qtsj.push({\n \"title\": \"书源设置\",\n \"type\": \"button\",\n \"action\": \"getHtmlSettings()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n })\n const excludeTitles = ['点击登录可切换来源', '切换后长按刷新即可'];\n style_list = style_list.filter(item => !excludeTitles.includes(item.title));\n } catch {}\n} else {\n\tif (islyc == '安卓'){\n\tqtsj.push({\n \"title\": \"下载阅读Sigma最新测试版\",\n \"url\": `{\\{java.longToast(\"本书源推荐使用最新版洛娅橙改版阅读Σ\\\\n请安装后重新导入书源\");java.startBrowser(\"https:\/\/legado.gyks.cf\",\"下载阅读Sigma最新测试版\");}}`,\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n })}\n\t}\n\nif (qttoken.length > 10) {\nqtsj.push({\n title: \"晴天书架\",\n url: qtsjurl,\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 1\n }\n})\n} else {\nqtsj.push({\n title: \"登录晴天书源\",\n url: `{\\{java.startBrowser(\"${base_url}\/login\",\"登录书源\");}}`,\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 1\n }\n})\n}\n\n\nvar finalData = infoData.concat(groupDatas, style_list);\nfinalData = qtsj.concat(finalData);\nJSON.stringify(finalData);\n<\/js>",
"jsLib": "\/\/ 当前书源版本号,切勿修改,否则影响更新的识别\nlet localVersion = '26.5.31.1';\n\n\/\/ 初始服务器列表\nlet hosts = [\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];\n\n\n\/\/ 初始化值\nconst defaultConfig = {\n 线路: hosts[0],\n 发现页来源: \"番茄\",\n 发现页类型: \"小说\"\n};\n\n\/\/ 获取云端配置\nfunction getCloudSettings(r) {\n if (r == undefined) r = false;\n const {\n java,\n cache\n } = this;\n let c = this.getVariable('云端配置').version;\n if (r || !c) {\n java.longToast(`\\n正在更新最新配置`);\n try {\n let url = `\/static\/source_config\/config.json`;\n \/\/\tjava.longToast(url)\n let js = this.request(url);\n js = JSON.parse(String(js));\n let intc = parseInt(c);\n let intv = parseInt(js['version']);\n if (intc >= intv) {\n \t java.longToast(`\\n已是最新配置:${js['version']}`);\n \t return;\n \t} else if (!intc && intv) {\n \t\tjava.longToast(`\\n已初始化配置:${intv}`);\n \t\t} else if (intc < intv || !intc && intv) {\n java.longToast(`\\n已更新配置:${intc}→${intv}`);\n } else {\n java.longToast(`\\n获取配置失败:请切换线路再试试~`);\n return;\n }\n this.setVariable('云端配置', js);\n \n return js;\n } catch (e) {\n java.longToast(`\\n获取最新配置失败:${e}`);\n }\n }\n}\n\n\/\/ 统一获取和解析变量\nfunction _getParsedVariable() {\n const {\n source,\n java\n } = this;\n let v = source.getVariable();\n try {\n return JSON.parse(v);\n } catch {\n return defaultConfig;\n }\n}\n\n\/\/ 获取设置变量\nfunction getLoginInfo(k) {\n const {\n source,\n java\n } = this;\n let res = source.getLoginInfoMap();\n if (!res) {\n res = defaultConfig;\n }\n if (res[k] == '❌') {\n return '0';\n } else if (res[k] == '✅') {\n return '1';\n }\n return res[k]\n}\n\n\/\/ 还原\nfunction deleteVariable() {\n const {\n source,\n java,\n cache\n } = this;\n source.setVariable('');\n cache.delete('gyksconfig');\n java.longToast('\\n所有设置已还原到初始化导入状态');\n}\n\n\/\/ 获取源变量\nfunction getVariable(k) {\n if (k == undefined) k = \"\";\n const { source } = this;\n let parsed = {}\n try {\n parsed = JSON.parse(source.getVariable());\n } catch {}\n if (k == \"\") {\n return parsed;\n }\n let value = parsed[k];\n if (value == undefined) {\n value = defaultConfig[k];\n }\n return value != undefined ? value : \"\";\n}\n\n\/\/ 设置源变量\nfunction setVariable(k, v, t) {\n if (t == undefined) t = true;\n const {\n source,\n java\n } = this;\n const vs = this.getVariable();\n vs[k] = v;\n source.setVariable(JSON.stringify(vs, null, 4));\n if (k != '云端配置' && t) {\n java.toast(`\\n设置 ${k} 为 ${v}`)\n }\n}\n\n\/\/ 获取正在使用的线路\nfunction BaseUrl() {\n let hostsbk = this.getVariable('云端配置')['hosts'] || hosts;\n let h = this.getVariable(\"线路\");\n if (!h || String(h) == \"undefind\") {\n h = hostsbk[0]\n }\n return h;\n}\n\n\/\/ 切换线路\nfunction switchToNextLine(toast) {\n if (toast == undefined) toast = false;\n const currentLine = this.BaseUrl();\n let hostsbk = this.getVariable('云端配置')['hosts'] || hosts;\n let currentIndex = hostsbk.findIndex(item => item == currentLine);\n let nextIndex = (currentIndex + 1) % hostsbk.length;\n const nextLine = hostsbk[nextIndex];\n this.setVariable(\"线路\", nextLine,false);\n let switchToast = this.getVariable(\"切换提醒\") || \"true\";\n if (toast && switchToast == \"true\") {\n this.java.longToast(`\\n${toast}\\n自动切换到:${nextLine}`)\n }\n return nextLine;\n}\n\n\n\/\/ 备用设置方案\nfunction ste(v) {\n const {\n java\n } = this;\n let moreSettings = this.getVariable(\"更多设置\") || {};\n let paraSettings = this.getVariable(\"段评设置\") || {};\n if (v==\"段评\"){\n if (paraSettings[\"段评开关\"] != \"false\"){\n paraSettings[\"段评开关\"]= \"false\";\n java.longToast(\"\\n❌段评已关闭\");\n }else{\n paraSettings[\"段评开关\"]= \"true\";\n java.longToast(\"\\n✅段评已开启\");\n }\n }\n if (v==\"小说\" || v==\"听书\" || v==\"漫画\" || v==\"短剧\"){\n moreSettings[\"搜索模式\"]=v;\n java.longToast(`\\n已设置搜索模式为 ${v}`);\n }\n if (v==\"图片\"){\n if (moreSettings[\"显示图片\"] != \"false\"){\n moreSettings[\"显示图片\"]= \"false\";\n java.longToast(\"\\n❌已关闭图片显示\");\n }else{\n moreSettings[\"显示图片\"]= \"true\";\n java.longToast(\"\\n✅已开启图片显示\");\n }\n }\n if (v==\"简介\"){\n if (moreSettings[\"完整简介\"] != \"false\"){\n moreSettings[\"完整简介\"]= \"false\";\n java.longToast(\"\\n❌已关闭完整简介\");\n }else{\n moreSettings[\"完整简介\"]= \"true\";\n java.longToast(\"\\n✅已开启完整简介\");\n }\n }\n if (v==\"同步\"){\n if (moreSettings[\"同步书架\"] != \"false\"){\n moreSettings[\"同步书架\"]= \"false\";\n java.longToast(\"\\n❌已关闭同步书架\");\n }else{\n moreSettings[\"同步书架\"]= \"true\";\n java.longToast(\"\\n✅已开启同步书架\");\n }\n }\n if (v==\"强制\"){\n if (moreSettings[\"强制搜索\"] != \"false\"){\n moreSettings[\"强制搜索\"]= \"false\";\n java.longToast(\"\\n❌已关闭强制搜索\");\n }else{\n moreSettings[\"强制搜索\"]= \"true\";\n java.longToast(\"\\n✅已开启强制搜索\");\n }\n }\n if (v==\"目录\"){\n if (moreSettings[\"目录显示来源\"] != \"false\"){\n moreSettings[\"目录显示来源\"]= \"false\";\n java.longToast(\"\\n❌已关闭目录显示来源\");\n }else{\n moreSettings[\"目录显示来源\"]= \"true\";\n java.longToast(\"\\n✅已开启目录显示来源\");\n }\n }\n if (v==\"网络\"){\n if (moreSettings[\"网络模式\"] != \"服务器\"){\n moreSettings[\"网络模式\"]= \"服务器\";\n java.longToast(\"\\n已切换为 服务器 模式\");\n }else{\n moreSettings[\"网络模式\"]= \"本地\";\n java.longToast(\"\\n已切换为 本地 模式\");\n }\n }\n this.setVariable(\"更多设置\", moreSettings, false);\n this.setVariable(\"段评设置\", paraSettings, false);\n}\n\nfunction parseBadMap(str) {\n str = str\n .replace(\/^{|}$\/g, '');\n\n const result = {};\n\n str.split(',').forEach(item => {\n const parts = item.split('=');\n\n if (parts.length === 2) {\n const key = parts[0].trim();\n const value = parts[1].trim();\n\n result[key] = value;\n }\n });\n\n return result;\n}\n\nfunction findItemIndex(data, item_id) {\n if (!data || !Array.isArray(data)) {\n return 0;\n }\n for (let i = 0; i < data.length; i++) {\n if (data[i] && data[i].item_id == item_id) {\n return i;\n }\n }\n return 0;\n}\n\n\/\/ 判断当前环境\nfunction checkEnv() {\n const {\n java,\n source\n } = this;\n\n try {\n java.qread();\n return \"轻阅读\";\n } catch (e) {}\n\n try {\n if (typeof java.reLoginView == 'function') {\n return \"改版\";\n }\n new Packages.io.legato.kazusa.utils.TimeoutCancellationException('');\n return \"改版\";\n } catch (e) {}\n try {\n java.deviceID();\n return \"苹果\";\n } catch (e) {}\n\n if (typeof source.loginUi == 'function') {\n return \"安卓\";\n }\n\n return \"改版\";\n}\n\n\/\/ 获取番茄token\nfunction getFqToken() {\n const {\n java,\n cookie\n } = this;\n\n try {\n let cookieValue = String(cookie.getCookie('fanqienovel.com')) || String(java.getCookie('fanqienovel.com'));\n let parts = cookieValue.split(\";\");\n for (let part of parts) {\n if (part.includes(\"sessionid\")) {\n return part.split(\"=\")[1];\n }\n }\n } catch {}\n return \"\";\n}\n\n\/\/ 获取登陆token\nfunction getToken() {\n const {\n java,\n cookie\n } = this;\n let hostsbk = this.getVariable('云端配置')['hosts'] || hosts;\n try {\n for (let h of hostsbk) {\n let cookieValue = String(cookie.getCookie(h)) || String(java.getCookie(h));\n let parts = cookieValue.split(\";\");\n for (let part of parts) {\n if (part.includes(\"qttoken\")) {\n return part.split(\"=\")[1];\n }\n }\n }\n } catch {}\n return \"\";\n}\n\n\/\/ 移除ck\nfunction removeAllCookies() {\n const {\n java,\n cookie\n } = this;\n let hostsbk = this.getVariable('云端配置')['hosts'] || hosts;\n for (let h of hostsbk) {\n cookie.removeCookie(h);\n }\n cookie.removeCookie('fanqienovel.com');\n java.toast('\\n已退出登陆')\n}\n\n\/\/ 设置ck\nfunction setAllCookies(ck) {\n const {\n cookie\n } = this;\n let hostsbk = this.getVariable('云端配置')['hosts'] || hosts;\n for (let h of hostsbk) {\n cookie.setCookie(h, ck);\n }\n}\n\n\/\/ 请求封装\nfunction request(url, method, body, req, index) {\n if (method == undefined) method = 'GET';\n if (body == undefined) body = {};\n if (req == undefined) req = true;\n if (index == undefined) index = 0;\n\n let urla = url;\n if (!url.includes('http')) {\n urla = this.BaseUrl() + url;\n }\n\n const { java } = this;\n let device = '';\n try {\n device = java.androidId();\n } catch {\n try {\n device = java.deviceID();\n } catch {}\n }\n\n let qttoken = this.getToken();\n let options = {\n method: method,\n headers: {\n 'cookie': `qttoken=${qttoken};deviceId=${device};`,\n 'Content-Type': 'application\/json'\n },\n body: JSON.stringify(body)\n };\n\n urla = `${urla},${JSON.stringify(options)}`;\n java.log(urla);\n\n if (!req) {\n return urla;\n }\n\n const startTime = new Date().getTime();\n\n let data;\n let requestTimedOut = false;\n let requestFailed = false;\n\n try {\n data = java.ajax(urla);\n JSON.parse(data);\n } catch (error) {\n requestFailed = true;\n } finally {\n const endTime = new Date().getTime();\n const duration = endTime - startTime;\n let timeout = this.getVariable('超时时间') || '5';\n timeout = parseInt(timeout)*1000\n if (!requestFailed && duration > timeout ) {\n requestTimedOut = true;\n }\n }\n if (requestTimedOut) {\n let timeoutSwitch = this.getVariable('超时自动切换') || 'true';\n if (timeoutSwitch!= \"false\") {\n java.log('检测到当前线路较慢,已切换线路');\n this.switchToNextLine('⚠️当前线路较慢');\n }\n return data;\n }\n if (requestFailed) {\n java.log('线路报错,自动切换下一个');\n let hostsbk = this.getVariable('云端配置')['hosts'] || hosts;\n let autoSwitch = this.getVariable('自动切换') || 'true';\n\n if (hostsbk.length > index && autoSwitch != \"false\") {\n this.switchToNextLine('❌线路报错');\n return this.request(url, method, body, req, index + 1);\n } else {\n return \"\";\n }\n }\n return data;\n}\n\nfunction parseJsonSafely(str, defaultValue) {\n if (defaultValue == null) defaultValue = null;\n try {\n return JSON.parse(str);\n } catch (e) {\n return defaultValue;\n }\n}\n\n\n\/\/去除图片\nfunction removeAllImgTags(htmlString) {\n const imgTagRegex = \/<img\\b[^>]*>|<\\\/img>|<img\\b[^>]*\\\/>\/gi;\n return htmlString.replace(imgTagRegex, '');\n}\n\n\/\/ 段评气泡生成\nfunction paraForAndroid(content, sources) {\n let {\n java,\n cache,\n source\n } = this;\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, encodedUrl,sources)}\"><\/p>`;\n } else {\n return `<p>${text}<\/p>`;\n }\n }\n );\n}\n\n\nlet svgs = [\n \"<svg t='1760002253572' class='icon' viewBox='-150 0 1224 1224' version='1.1' xmlns='http:\/\/www.w3.org\/2000\/svg' p-id='1490' width='800' height='800'><g transform='rotate(90 512 512) scale(1 1.2)'><path d='M224 149.333333h576c40.533333 0 74.666667 34.133333 74.666667 74.666667v460.8c0 40.533333-34.133333 74.666667-74.666667 74.666667h-108.8c-40.533333 0-78.933333 17.066667-104.533334 46.933333l-49.066666 55.466667c-4.266667 6.4-14.933333 10.666667-23.466667 10.666667-10.666667 0-19.2-4.266667-25.6-10.666667l-46.933333-55.466667c-25.6-29.866667-64-46.933333-104.533334-46.933333h-110.933333c-40.533333 0-74.666667-34.133333-74.666667-74.666667V224c0-40.533333 34.133333-74.666667 74.666667-74.666667z' fill='$tccolor' p-id='1529'><\/path><path d='M512 938.666667c-27.733333 0-55.466667-12.8-72.533333-34.133334l-46.933334-55.466666c-14.933333-17.066667-34.133333-25.6-57.6-25.6h-110.933333c-76.8 0-138.666667-61.866667-138.666667-138.666667V224C85.333333 147.2 147.2 85.333333 224 85.333333h573.866667c76.8 0 138.666667 61.866667 138.666666 138.666667v460.8c0 76.8-61.866667 138.666667-138.666666 138.666667h-108.8c-21.333333 0-42.666667 8.533333-55.466667 25.6l-49.066667 55.466666c-17.066667 21.333333-44.8 34.133333-72.533333 34.133334zM224 149.333333C183.466667 149.333333 149.333333 183.466667 149.333333 224v460.8c0 40.533333 34.133333 74.666667 74.666667 74.666667h110.933333c40.533333 0 78.933333 17.066667 104.533334 49.066666l46.933333 55.466667c6.4 6.4 14.933333 10.666667 25.6 10.666667 8.533333 0 19.2-4.266667 23.466667-10.666667l49.066666-55.466667c25.6-29.866667 64-46.933333 104.533334-46.933333h108.8c40.533333 0 74.666667-34.133333 74.666666-74.666667V224C874.666667 183.466667 840.533333 149.333333 800 149.333333h-576z' fill='$color' p-id='1530'><\/path> <\/g><text x='460' y='650' font-family='Arial, sans-serif' text-anchor='middle' font-size='400' fill='$fontcolor'>$displayText<\/text><\/svg>\",\n\n \"<svg xmlns='http:\/\/www.w3.org\/2000\/svg' width='100' height='90' viewBox='0 0 25 17' fill='none' style='color: $color; opacity: 1;'><path stroke='currentColor' stroke-width='1.00' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='4.00' style='stroke: $color; stroke-opacity: 0.5; stroke-width: 1px; stroke-linejoin: round; fill: $tccolor; fill-opacity: 0.5;' d='M 7.5 0.5 L 16.5 0.5 A 7 7 0 0 1 23.5 7.5 L 23.5 7.5 A 7 7 0 0 1 16.5 14.5 L 8.5 14.5 L 5.192 16.5 L 5.5 14.5 A 7 7 30 0 1 0.5 7.5 L 0.5 7.5 A 7 7 0 0 1 7.5 0.5 Z'\/><text fill='$fontcolor' font-family='Arial, sans-serif' font-size='10' font-weight='500' text-anchor='middle' dy='0.35em' x='12.00' y='7.50'>$displayText<\/text><\/svg>\",\n\n \"<svg xmlns='http:\/\/www.w3.org\/2000\/svg' width='100' height='90' viewBox='0 0 25 17' fill='none' style='color: $color; opacity: 1;'><path d='M24 14.5v-12a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v7.528a2 2 0 0 1-.211.894l-2.065 4.13a1 1 0 0 0 .894 1.448H22a2 2 0 0 0 2-2z' stroke='currentColor' style='stroke: $color; stroke-opacity: 0.5; stroke-width: 1px; stroke-linejoin: round; fill: $tccolor; fill-opacity: 0.5;'><\/path><text x='13.5' y='12' text-anchor='middle' alignment-baseline='auto' font-size='10' fill='currentColor' font-family='Arial, sans-serif' font-weight='500' style='fill: $fontcolor; opacity: 1;'>$displayText<\/text><\/svg>\",\n\n\n\n \"<svg xmlns='http:\/\/www.w3.org\/2000\/svg' width='100' height='90' viewBox='6 10 88 76' style='color: $color; opacity: 1;'><path d='M12,12 L88,12 Q92,12 92,16 L92,68 Q92,72 88,72 L28,72 L12,84 L12,72 Q8,72 8,68 L8,16 Q8,12 12,12 Z' stroke='currentColor' stroke-width='4' stroke-linejoin='round' style='stroke: $color; stroke-opacity: 0.5; stroke-width: 2.5px; stroke-linejoin: round; fill: $tccolor; fill-opacity: 0.5;'><\/path><text x='50' y='60' font-family='Arial, sans-serif' text-anchor='middle' font-size='45' fill='currentColor' font-weight='500' alignment-baseline='auto' style='fill: $fontcolor; opacity: 1;'>$displayText<\/text><\/svg>\",\n\n\n\n \"<svg xmlns='http:\/\/www.w3.org\/2000\/svg' width='100' height='90' viewBox='0 0 25 17' fill='none' style='color: $color; opacity: 1;'><rect x='0.5' y='0.5' width='24' height='16' rx='2.5' ry='2.5' stroke='currentColor' style='stroke: $color; stroke-opacity: 0.5; stroke-width: 1px; stroke-linejoin: round; fill: $tccolor; fill-opacity: 0.5;'><\/rect><text x='12.5' y='12' text-anchor='middle' alignment-baseline='auto' font-size='10' fill='currentColor' font-family='Arial, sans-serif' font-weight='500' style='fill: $fontcolor; opacity: 1;'>$displayText<\/text><\/svg>\",\n\n\n\n\n \"<svg xmlns='http:\/\/www.w3.org\/2000\/svg' width='100' height='90' viewBox='0 0 25 17' fill='none' style='color: $color; opacity: 1;'><circle cx='12.5' cy='8.5' r='8' stroke='currentColor' style='stroke: $color; stroke-opacity: 0.5; stroke-width: 1px; stroke-linejoin: round; fill: $tccolor; fill-opacity: 0.5;'><\/circle><text x='13' y='12' text-anchor='middle' alignment-baseline='auto' font-size='10' fill='currentColor' font-family='Arial, sans-serif' font-weight='500' style='fill: $fontcolor; opacity: 1;'>$displayText<\/text><\/svg>\"\n];\n\n\n\/\/ 创建svg\nfunction createSvg(number, encodedUrl, sources) {\n var displayText = number > 99 ? \"99+\" : number.toString();\n let data = this.getVariable('段评设置');\n let svgstyle = data['段评样式'] || \"0\";\n let color = data['段评边框颜色'] || \"#A7A7A7\";\n let tccolor = data['段评填充颜色'] || \"transparent\";\n let fontcolor = data['段评字体颜色'] || \"#A7A7A7\";\n let zdy = svgs[0];\n try {\n zdy = data['自定义段评样式'][svgstyle] || svgs[0];\n } catch {}\n const styleMap = {\n \"0\": svgs[0],\n \"1\": svgs[1],\n \"2\": svgs[2],\n \"3\": svgs[3],\n \"4\": svgs[4],\n \"5\": svgs[5],\n };\n let svg = styleMap[svgstyle] || zdy;\n svg = svg.replace('$color', color)\n .replace('$tccolor', tccolor)\n .replace('$fontcolor', fontcolor)\n .replace('$displayText', displayText);\n var jc = 'js';\n let islyc = this.checkEnv()\n if (islyc == \"改版\") {\n jc = 'click'\n }\n let style = data['气泡增大'] == \"true\" ? 'TEXT' : 'text';\n var encodedSvg = this.java.base64Encode(svg);\n return `data:image\/svg+xml;base64,${encodedSvg},{\"${jc}\":\"showCmt('${encodedUrl}', '${sources}' )\",\"style\":\"${style}\"}`;\n}\n\n\n\n\n\/\/ 显示弹窗\nfunction showCmt(url, sources) {\n let {\n java,\n cache\n } = this;\n const currentTime = Date.now();\n const click = cache.getFromMemory(url);\n let islyc = this.checkEnv()\n if (click < 1 && islyc == \"安卓\") {\n cache.putMemory(url, click + 1);\n return;\n } else {\n if (islyc == \"轻阅读\") {\n java.startBrowserDp(url, sources + '段评');\n } else if (islyc == \"改版\") {\n let paradata = new Date().toLocaleDateString();\n let htmlContent = cache.get(paradata);\n if (!htmlContent || String(htmlContent).indexOf(\"评论\") == -1) {\n java.longToast(`\\n${paradata} 初始化段评页面,请稍等~`);\n try {\n htmlContent = java.ajax(url);\n } catch {\n\n }\n\n cache.put(paradata, htmlContent);\n java.longToast(`\\n${paradata} 初始化段评页面成功~`);\n }\n java.showBrowser(\n url,\n htmlContent,\n `window.java=java;`,\n JSON.stringify({\n \"expandedCornersRadius\": 20,\n \"dismissOnTouchOutside\": true,\n \"isDraggable\": true,\n \"shouldDimBackground\": true,\n \"backgroundDimAmount\": 0.5,\n \"hardwareAccelerated\": true,\n \"isNestedScrollingEnabled\": true,\n \"isGestureInsetBottomIgnored\": true,\n \"setFitToContents\": true,\n \"isHideable\": true,\n \"heightPercentage\": 0.8\n })\n );\n } else {\n java.startBrowser(url, sources + '段评');\n }\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 `<div rs-native>${text}<comment count=\"${count}\" onPress=\"java.showReadingBrowser('${encodedUrl}','${sources}段评')\"><\/div>`;\n } else {\n return `<div rs-native>${text}<\/div>`;\n }\n }\n );\n}\n\n\n\n\/\/按钮切换时改变源变量并刷新发现\nfunction show(m, t) {\n const {\n java,\n source\n } = this;\n let data = {};\n try {\n data = JSON.parse(source.getVariable())\n } catch {}\n data[t] = m;\n if (t == \"发现页类型\") {\n data[\"发现页来源\"] = '番茄';\n }\n source.setVariable(JSON.stringify(data, null, 2));\n java.refreshExplore();\n}\n\nfunction createFilter(title, chars, defaultVal, paramKey, size, viewName) {\n if (viewName == undefined) viewName = \"'请选择类型'\";\n return {\n title: title,\n type: \"select\",\n chars: chars,\n default: defaultVal,\n \"viewName\": viewName,\n action: `show(infoMap['${title}'],'${paramKey}')`,\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: size\n }\n };\n}\n\nfunction createLoginFilter(title, chars, defaultVal, paramKey, size, viewName) {\n const {\n source,\n java\n } = this;\n if (viewName == undefined) viewName = \"'请选择类型'\";\n \n let loginMap = source.getLoginInfoMap();\n \/\/java.longToast(loginMap);\n return {\n name: title,\n type: \"select\",\n chars: chars,\n default: defaultVal,\n \"viewName\": viewName,\n action: `show('${loginMap[title]}','${paramKey}')`,\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: size\n }\n };\n}\n\n\/\/ 书源更新\nfunction renderVersionPage() {\n let {\n java\n } = this;\n let hostsbk = this.getVariable('云端配置')['hosts'] || hosts;\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>推荐使用阅读Sigma版本<br>正式版可能存在兼容性问题-<a href='https:\/\/legado.gyks.cf' style='text-decoration: none; color: #2D9D78; font-weight: 600;'>下载<\/a><\/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 let hosts = \\`${hostsbk}\\`;\n hosts = hosts.split(',');\n \nconst icons = ['box', 'satellite', 'link', 'bolt', 'globe', 'broadcast-tower'];\n\n\/\/ 初始化serverConfig\nconst serverConfig = {\n main: {\n name: '主线路导入',\n icon: 'rocket',\n baseUrl: 'https:\/\/sy.gyks.cf',\n downloadPath: '\/download\/光遇聚合.json'\n }\n};\n\nhosts.forEach((host, index) => {\n const backupNumber = index + 1;\n const key =\\`备用线路\\${backupNumber}\\`;\n\n serverConfig[key] = {\n name: \\`\\${key}导入\\`,\n icon: icons[index] || 'broadcast-tower',\n baseUrl: host,\n downloadPath: '\/sy\/download\/光遇聚合.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\nasync function fetchVersionData() {\n const serversToCheck = Object.values(serverConfig);\n \n for (const server of serversToCheck) {\n try {\n const apiPath = server.baseUrl.includes('sy.gyks.cf') \n ? '\/fils-groups' \n : '\/get_sy_log';\n \n const response = await fetch(server.baseUrl + apiPath, { 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\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();\nconst legadoData = (data.data && data.data.find(item => item.key == 'legado_aggregate')) || {};\nconst cloudVersion = String(legadoData.current_version || '');\nconst updateLog = legadoData.update_log || [];\n\n\/\/ 显示版本信息\ncurrentVersion.textContent = \\`v\\${localVer}\\`;\nlatestVersion.textContent = \\`v\\${cloudVersion}\\`;\n\n\/\/ 处理日志(update_log 是数组格式)\nif (updateLog.length > 0) {\n\/\/ 显示最新日志\nconst latestLog = updateLog[0];\nlatestLogDate.innerHTML = \\`<i class=\"fas fa-calendar-alt\"><\/i> v\\${latestLog.version}\\`;\nlatestLogContent.innerHTML = latestLog.log;\nlatestLogContainer.style.display = 'block';\n\n\/\/ 显示历史日志\nif (updateLog.length > 1) {\nconst historyLogs = updateLog.slice(1);\nhistoryCount.textContent = \\`(\\${historyLogs.length}条)\\`;\nlogList.innerHTML = historyLogs.map(item => \\`\n<div class=\"history-item\">\n <div class=\"history-date\">\n <i class=\"fas fa-calendar-day\"><\/i>\n <span>v\\${item.version}<\/span>\n <\/div>\n <div class=\"history-text\">\\${item.log}<\/div>\n<\/div>\n\\`).join('');\nlogsContainer.style.display = 'block';\n}\n}\n\n\/\/ 检查更新状态\nconst compareResult = compareVersions(cloudVersion);\n\n\/\/ 显示版本指示器\nversionIndicator.style.display = 'flex';\n\nif (compareResult === -1) {\n\/\/ 需要更新\ncurrentStatus.textContent = '待更新';\ncurrentStatus.className = 'version-status status-outdated';\nversionIndicator.className = 'version-indicator update-needed';\nversionIndicator.innerHTML = '<i class=\"fas fa-arrow-right\"><\/i>';\n\n\/\/ 使用统一配置生成下载按钮\nbuttonGroup.innerHTML = Object.values(serverConfig).map(server => {\nconst fullUrl = server.baseUrl + server.downloadPath;\nreturn \\`\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('');\nbuttonGroup.style.display = 'flex';\n} else {\n\/\/ 已是最新版本\ncurrentStatus.textContent = '最新';\ncurrentStatus.className = 'version-status status-latest';\nversionIndicator.className = 'version-indicator is-latest';\nversionIndicator.innerHTML = '<i class=\"fas fa-check\"><\/i>';\n\nstatusAlert.className = 'status-alert up-to-date';\nstatusAlert.innerHTML = '<i class=\"fas fa-check-circle\"><\/i> <div>您已是最新版本<\/div>';\nstatusAlert.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}\n\n\n\/\/ 段评设置页面\nfunction getSvgSettings() {\n const {\n java\n } = this;\n const islyc = this.checkEnv();\n if (islyc == \"苹果\") {\n java.longToast(\"\\n当前软件不支持设置段评样式\");\n return;\n }\n let data = this.getVariable('段评设置');\n\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,maximum-scale=1,user-scalable=no\">\n<title>光遇小说 - 段评设置<\/title>\n<link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.4.0\/css\/all.min.css\">\n<style>\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n -webkit-tap-highlight-color: transparent;\n}\n\n:root {\n --color-primary: #2D9D78;\n --color-primary-light: #3DB893;\n --color-primary-dark: #1E6F55;\n --color-accent: #FFB74D;\n --color-success: #4CAF50;\n --color-error: #F44336;\n --color-warning: #FFC107;\n --color-info: #2196F3;\n \n --bg-dark: #f5f7fa;\n --bg-darker: #ffffff;\n --bg-card: rgba(255, 255, 255, 0.95);\n --bg-elevated: #f8fafb;\n \n --text-primary: #1a1d23;\n --text-secondary: #6b7280;\n --text-tertiary: #9ca3af;\n \n --border-color: rgba(0, 0, 0, 0.08);\n \n --font-display: 'Libre Baskerville', serif;\n --font-body: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n}\n\n[data-theme=\"dark\"] {\n --bg-dark: #0A1628;\n --bg-darker: #050B14;\n --bg-card: #142235;\n --bg-elevated: #1A2B42;\n \n --text-primary: #FFFFFF;\n --text-secondary: rgba(255, 255, 255, 0.7);\n --text-tertiary: rgba(255, 255, 255, 0.5);\n \n --border-color: rgba(255, 255, 255, 0.1);\n}\n\nhtml, body {\n width: 100%;\n min-height: 100vh;\n font-family: var(--font-body);\n font-size: 16px;\n color: var(--text-primary);\n background: var(--bg-darker);\n overflow-x: hidden;\n}\n\n.container {\n padding: 20px;\n max-width: 800px;\n margin: 0 auto;\n}\n\n.main-card {\n background: var(--bg-card);\n border-radius: 20px;\n box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1);\n border: 1px solid var(--border-color);\n overflow: hidden;\n}\n\/* 提示文案样式 *\/\n.tip-alert {\n background: linear-gradient(135deg, rgba(255, 193, 7, 0.12) 0%, rgba(255, 152, 0, 0.08) 100%);\n border: 1px solid rgba(255, 193, 7, 0.25);\n border-radius: 16px;\n padding: 16px 18px;\n margin: 20px;\n display: flex;\n align-items: flex-start;\n gap: 14px;\n box-shadow: 0 4px 16px rgba(255, 193, 7, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.8);\n position: relative;\n overflow: hidden;\n}\n\n.tip-alert::before {\n content: '';\n position: absolute;\n top: -50%;\n right: -20%;\n width: 100px;\n height: 100px;\n background: rgba(255, 193, 7, 0.1);\n border-radius: 50%;\n}\n\n.tip-alert i {\n color: #f97316;\n font-size: 22px;\n flex-shrink: 0;\n margin-top: 2px;\n position: relative;\n z-index: 1;\n}\n\n.tip-alert p {\n font-size: 13px;\n color: var(--text-secondary);\n line-height: 1.6;\n margin: 0;\n position: relative;\n z-index: 1;\n}\n\n.tip-alert .highlight-text {\n color: #ea580c;\n font-weight: 700;\n}\n\/* 设置项样式 *\/\n.setting-item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px;\n border-bottom: 1px solid var(--border-color);\n}\n\n.setting-item:last-child {\n border-bottom: none;\n}\n\n.setting-info {\n flex: 1;\n min-width: 0;\n}\n\n.setting-title {\n font-size: 15px;\n font-weight: 700;\n color: var(--text-primary);\n margin-bottom: 4px;\n}\n\n.setting-desc {\n font-size: 12px;\n color: var(--text-secondary);\n line-height: 1.4;\n}\n\n\/* 开关样式 *\/\n.switch {\n position: relative;\n display: inline-block;\n width: 48px;\n height: 28px;\n flex-shrink: 0;\n}\n\n.switch input {\n opacity: 0;\n width: 0;\n height: 0;\n}\n\n.slider {\n position: absolute;\n cursor: pointer;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: #ccc;\n transition: 0.3s;\n border-radius: 28px;\n}\n\n.slider:before {\n position: absolute;\n content: \"\";\n height: 22px;\n width: 22px;\n left: 3px;\n bottom: 3px;\n background-color: white;\n transition: 0.3s;\n border-radius: 50%;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);\n}\n\ninput:checked + .slider {\n background: linear-gradient(135deg, var(--color-primary), var(--color-primary-light));\n}\n\ninput:checked + .slider:before {\n transform: translateX(20px);\n}\n\n\/* 选择器样式 *\/\n.select-wrapper {\n position: relative;\n flex-shrink: 0;\n}\n\n.select-wrapper select {\n appearance: none;\n -webkit-appearance: none;\n padding: 8px 32px 8px 12px;\n font-size: 13px;\n font-weight: 600;\n color: var(--text-primary);\n background: var(--bg-elevated);\n border: 1px solid var(--border-color);\n border-radius: 8px;\n cursor: pointer;\n min-width: 120px;\n}\n\n.select-wrapper::after {\n content: '\\\\f078';\n font-family: 'Font Awesome 6 Free';\n font-weight: 900;\n position: absolute;\n right: 8px;\n top: 50%;\n transform: translateY(-50%);\n color: var(--text-secondary);\n pointer-events: none;\n font-size: 12px;\n}\n\n\/* 颜色选择器样式 *\/\n.color-picker-wrapper {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n}\n\n.color-input {\n width: 40px;\n height: 40px;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n background: transparent;\n padding: 2px;\n}\n\n.color-input::-webkit-color-swatch-wrapper {\n padding: 0;\n}\n\n.color-input::-webkit-color-swatch {\n border-radius: 6px;\n border: 2px solid var(--border-color);\n}\n\n.color-text-input {\n width: 100px;\n padding: 8px 10px;\n font-size: 12px;\n font-weight: 600;\n color: var(--text-primary);\n background: var(--bg-elevated);\n border: 1px solid var(--border-color);\n border-radius: 6px;\n text-transform: uppercase;\n}\n\n.transparent-btn {\n padding: 8px 12px;\n font-size: 11px;\n font-weight: 600;\n color: var(--color-primary);\n background: rgba(45, 157, 120, 0.1);\n border: 1px solid var(--color-primary);\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.transparent-btn:hover {\n background: rgba(45, 157, 120, 0.2);\n}\n\n\/* 预览区域 *\/\n\/* 自定义样式容器 *\/\n.custom-style-container {\n padding: 0 16px 16px;\n}\n\n.custom-style-container textarea {\n width: 100%;\n padding: 12px;\n font-size: 13px;\n font-family: monospace;\n color: var(--text-primary);\n background: var(--bg-card);\n border: 1px solid var(--border-color);\n border-radius: 8px;\n resize: vertical;\n min-height: 100px;\n box-sizing: border-box;\n}\n\n\/* 添加样式按钮 *\/\n.add-style-btn {\n width: 36px;\n height: 36px;\n border-radius: 50%;\n background: linear-gradient(135deg, var(--color-primary), var(--color-primary-light));\n border: none;\n color: white;\n font-size: 16px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 2px 8px rgba(45, 157, 120, 0.3);\n transition: all 0.2s ease;\n}\n\n.add-style-btn:hover {\n transform: scale(1.1);\n box-shadow: 0 4px 12px rgba(45, 157, 120, 0.4);\n}\n\n\/* 自定义样式区域 *\/\n.custom-style-section {\n padding: 0 16px 16px;\n}\n\n.custom-styles-container {\n margin-top: 12px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.empty-custom-styles {\n text-align: center;\n padding: 20px;\n color: var(--text-secondary);\n font-size: 13px;\n background: var(--bg-elevated);\n border-radius: 12px;\n border: 1px dashed var(--border-color);\n}\n\n\/* 自定义样式卡片 *\/\n.custom-style-card {\n background: var(--bg-elevated);\n border-radius: 12px;\n padding: 12px;\n border: 1px solid var(--border-color);\n}\n\n.custom-style-header {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 10px;\n}\n\n.style-name-input {\n flex: 1;\n padding: 6px 10px;\n font-size: 13px;\n font-weight: 600;\n color: var(--text-primary);\n background: var(--bg-card);\n border: 1px solid var(--border-color);\n border-radius: 6px;\n}\n\n.style-actions {\n display: flex;\n gap: 6px;\n}\n\n.style-btn {\n width: 28px;\n height: 28px;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n transition: all 0.2s ease;\n}\n\n.preview-btn {\n background: rgba(45, 157, 120, 0.1);\n color: var(--color-primary);\n}\n\n.preview-btn:hover {\n background: rgba(45, 157, 120, 0.2);\n}\n\n.delete-btn {\n background: rgba(244, 67, 54, 0.1);\n color: #f44336;\n}\n\n.delete-btn:hover {\n background: rgba(244, 67, 54, 0.2);\n}\n\n.style-code-textarea {\n width: 100%;\n padding: 10px;\n font-size: 12px;\n font-family: monospace;\n color: var(--text-primary);\n background: var(--bg-card);\n border: 1px solid var(--border-color);\n border-radius: 8px;\n resize: vertical;\n min-height: 80px;\n box-sizing: border-box;\n margin-bottom: 10px;\n}\n\n.style-preview-small {\n display: flex;\n justify-content: center;\n padding: 10px;\n background: var(--bg-card);\n border-radius: 8px;\n}\n\n.preview-svg-container svg {\n width: 50px;\n height: 45px;\n}\n\n.preview-section {\n padding: 20px;\n background: var(--bg-elevated);\n border-top: 1px solid var(--border-color);\n}\n\n.preview-title {\n font-size: 14px;\n font-weight: 700;\n color: var(--text-primary);\n margin-bottom: 16px;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.preview-title i {\n color: var(--color-primary);\n}\n\n.preview-container {\n display: flex;\n flex-wrap: wrap;\n gap: 16px;\n justify-content: center;\n align-items: center;\n min-height: 150px;\n}\n\n.preview-item {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 8px;\n padding: 12px;\n background: var(--bg-card);\n border-radius: 12px;\n border: 1px solid var(--border-color);\n}\n\n.preview-item.selected {\n border-color: var(--color-primary);\n background: rgba(45, 157, 120, 0.05);\n}\n\n.preview-item svg {\n width: 60px;\n height: 54px;\n cursor: pointer;\n transition: transform 0.2s ease;\n}\n\n.preview-item svg:hover {\n transform: scale(1.1);\n}\n\n.preview-item .preview-label {\n font-size: 11px;\n color: var(--text-secondary);\n text-align: center;\n}\n\n.preview-item.selected .preview-label {\n color: var(--color-primary);\n font-weight: 600;\n}\n\n\/* 高亮文本 *\/\n.highlight {\n color: var(--color-primary);\n font-weight: 700;\n}\n\n.highlight-red {\n color: #ef4444;\n font-weight: 700;\n}\n\n\/* 响应式 *\/\n@media (max-width: 600px) {\n .container {\n padding: 12px;\n }\n \n .main-card {\n border-radius: 16px;\n }\n\n .tip-alert {\n margin: 16px;\n padding: 14px 16px;\n }\n \n .setting-item {\n padding: 14px;\n }\n \n .color-picker-wrapper {\n flex-direction: column;\n align-items: flex-end;\n }\n}\n<\/style>\n<\/head>\n<body data-theme=\"light\">\n<div class=\"container\">\n <div class=\"main-card\">\n <div class=\"tip-alert\">\n <p>设置完成后请点击右上角的<span class=\"highlight-text\">√<\/span>应用设置<\/p>\n <\/div>\n <!-- 设置项 -->\n <div class=\"setting-item\">\n <div class=\"setting-info\">\n <div class=\"setting-title\">段评开关<\/div>\n <div class=\"setting-desc\">仅 <span class=\"highlight-red\">番茄<\/span>、<span class=\"highlight\">七猫<\/span>、<span class=\"highlight-red\">塔读<\/span>、<span class=\"highlight\">QQ阅读<\/span> 支持<\/div>\n <\/div>\n <label class=\"switch\">\n <input type=\"checkbox\" id=\"duanpingSwitch\" checked>\n <span class=\"slider\"><\/span>\n <\/label>\n <\/div>\n \n <div class=\"setting-item\">\n <div class=\"setting-info\">\n <div class=\"setting-title\">气泡增大<\/div>\n <div class=\"setting-desc\">开启后,气泡显示会加大一些<\/div>\n <\/div>\n <label class=\"switch\">\n <input type=\"checkbox\" id=\"bubbleLargeSwitch\">\n <span class=\"slider\"><\/span>\n <\/label>\n <\/div>\n \n <div class=\"setting-item\">\n <div class=\"setting-info\">\n <div class=\"setting-title\">段评样式<\/div>\n <div class=\"setting-desc\">选择段评显示的样式风格,下方可实时预览效果<\/div>\n <\/div>\n <div class=\"select-wrapper\">\n <select id=\"duanpingStyle\">\n <option value=\"0\">起点样式<\/option>\n <option value=\"1\">番茄样式<\/option>\n <option value=\"2\">精致样式<\/option>\n <option value=\"3\">对话样式<\/option>\n <option value=\"4\">方框样式<\/option>\n <option value=\"5\">圆圈样式<\/option>\n <\/select>\n <\/div>\n <\/div>\n \n <div class=\"setting-item\">\n <div class=\"setting-info\">\n <div class=\"setting-title\">边框颜色<\/div>\n <div class=\"setting-desc\">设置段评边框颜色<\/div>\n <\/div>\n <div class=\"color-picker-wrapper\">\n <input type=\"color\" id=\"borderColor\" value=\"#A7A7A7\" class=\"color-input\">\n <input type=\"text\" id=\"borderColorText\" value=\"#A7A7A7\" class=\"color-text-input\">\n <\/div>\n <\/div>\n \n <div class=\"setting-item\">\n <div class=\"setting-info\">\n <div class=\"setting-title\">字体颜色<\/div>\n <div class=\"setting-desc\">设置段评字体颜色<\/div>\n <\/div>\n <div class=\"color-picker-wrapper\">\n <input type=\"color\" id=\"fontColor\" value=\"#A7A7A7\" class=\"color-input\">\n <input type=\"text\" id=\"fontColorText\" value=\"#A7A7A7\" class=\"color-text-input\">\n <\/div>\n <\/div>\n \n <div class=\"setting-item\">\n <div class=\"setting-info\">\n <div class=\"setting-title\">填充颜色<\/div>\n <div class=\"setting-desc\">设置段评背景填充颜色<\/div>\n <\/div>\n <div class=\"color-picker-wrapper\">\n <input type=\"color\" id=\"fillColor\" value=\"#FFFFFF\" class=\"color-input\">\n <input type=\"text\" id=\"fillColorText\" value=\"transparent\" class=\"color-text-input\">\n <button id=\"fillColorTransparent\" class=\"transparent-btn\">透明<\/button>\n <\/div>\n <\/div>\n \n <!-- 自定义样式管理 -->\n <div class=\"custom-style-section\">\n <div class=\"setting-item\">\n <div class=\"setting-info\">\n <div class=\"setting-title\">自定义样式管理<\/div>\n <div class=\"setting-desc\">点击下方\"+\"添加新的自定义样式,支持自定义名称<\/div>\n <\/div>\n <button id=\"addCustomStyleBtn\" class=\"add-style-btn\">\n <i class=\"fas fa-plus\"><\/i>\n <\/button>\n <\/div>\n \n <div id=\"customStylesContainer\" class=\"custom-styles-container\">\n <\/div>\n <\/div>\n \n <!-- 实时预览区域 -->\n <div class=\"preview-section\">\n <div class=\"preview-title\">\n <i class=\"fas fa-eye\"><\/i>\n <span>实时预览<\/span>\n <\/div>\n <div id=\"previewContainer\" class=\"preview-container\"><\/div>\n <\/div>\n <\/div>\n \n <!-- 隐藏的设置数据 -->\n <div id=\"hiddenSettings\" style=\"display: none;\">\n <div id=\"settingsDiv\">\n <span id=\"duanpingSwitchValue\">${(data && data.段评开关) || 'true'}<\/span>\n <span id=\"bubbleLargeSwitchValue\">${(data && data.气泡增大) || 'false'}<\/span>\n <span id=\"duanpingStyleValue\">${(data && data.段评样式) || '0'}<\/span>\n <span id=\"borderColorValue\">${(data && data.段评边框颜色) || '#A7A7A7'}<\/span>\n <span id=\"fontColorValue\">${(data && data.段评字体颜色) || '#A7A7A7'}<\/span>\n <span id=\"fillColorValue\">${(data && data.段评填充颜色) || 'transparent'}<\/span>\n <\/div>\n <textarea id=\"customStylesValue\" style=\"display: none;\">${typeof (data && data.自定义段评样式) == 'object' ? JSON.stringify(data.自定义段评样式) : ((data && data.自定义段评样式) || '{}')}<\/textarea>\n <\/div>\n<\/div>\n\n<script>\n\/\/ SVG模板数据\nconst SVG_TEMPLATES = ${JSON.stringify(svgs)};\nconst STYLE_NAMES = ['起点样式', '番茄样式', '精致样式', '对话样式', '方框样式', '圆圈样式'];\n\n\/\/ 当前设置\nlet settings = {\n '段评开关': '${(data && data.段评开关) || 'true'}',\n '气泡增大': '${(data && data.气泡增大) || 'false'}',\n '段评样式': '${typeof (data && data.自定义段评样式) == 'object' && (data && data.段评样式) ? data.段评样式 : ((data && data.段评样式) || '0')}',\n '段评边框颜色': '${(data && data.段评边框颜色) || '#A7A7A7'}',\n '段评字体颜色': '${(data && data.段评字体颜色) || '#A7A7A7'}',\n '段评填充颜色': '${(data && data.段评填充颜色) || 'transparent'}'\n};\n\n\/\/ 自定义样式对象 {\"自定义样式1\": \"<svg>...<\/svg>\", ...}\nlet customStyles = {};\nfunction initCustomStyles() {\n try {\n let customStylesStr = document.getElementById('customStylesValue').textContent;\n if (!customStylesStr) {\n customStylesStr = '{}';\n }\n customStyles = JSON.parse(htmlUnescape(customStylesStr));\n } catch (e) {\n customStyles = {};\n }\n}\n\n\/\/ HTML转义函数\nfunction htmlEscape(str) {\n if (!str) return str;\n return str.replace(\/&\/g, '&')\n .replace(\/<\/g, '<')\n .replace(\/>\/g, '>')\n .replace(\/\"\/g, '"')\n .replace(\/'\/g, ''');\n}\n\n\/\/ HTML反转义函数\nfunction htmlUnescape(str) {\n if (!str) return str;\n return str.replace(\/&\/g, '&')\n .replace(\/<\/g, '<')\n .replace(\/>\/g, '>')\n .replace(\/"\/g, '\"')\n .replace(\/'\/g, \"'\");\n}\n\n\/\/ 更新隐藏设置\nfunction updateHiddenSettings() {\n document.getElementById('duanpingSwitchValue').textContent = settings['段评开关'];\n document.getElementById('bubbleLargeSwitchValue').textContent = settings['气泡增大'];\n document.getElementById('duanpingStyleValue').textContent = settings['段评样式'];\n document.getElementById('borderColorValue').textContent = settings['段评边框颜色'];\n document.getElementById('fontColorValue').textContent = settings['段评字体颜色'];\n document.getElementById('fillColorValue').textContent = settings['段评填充颜色'];\n document.getElementById('customStylesValue').textContent = htmlEscape(JSON.stringify(customStyles));\n}\n\n\/\/ 获取下一个自定义样式名称\nfunction getNextStyleName() {\n let count = 1;\n while (customStyles['自定义样式' + count]) {\n count++;\n }\n return '自定义样式' + count;\n}\n\n\/\/ 添加自定义样式\nfunction addCustomStyle() {\n const name = getNextStyleName();\n customStyles[name] = '<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"100\" height=\"90\" viewBox=\"0 0 25 17\" fill=\"none\"><rect x=\"0.5\" y=\"0.5\" width=\"24\" height=\"16\" rx=\"2.5\" ry=\"2.5\" stroke=\"$color\" style=\"stroke-opacity: 0.5; fill: $tccolor; fill-opacity: 0.5;\"\/><text x=\"12.5\" y=\"12\" text-anchor=\"middle\" font-size=\"10\" fill=\"$fontcolor\" font-family=\"Arial, sans-serif\">$displayText<\/text><\/svg>';\n renderCustomStyles();\n updateHiddenSettings();\n updateStyleSelector();\n renderPreview();\n}\n\n\/\/ 获取选中的样式名称\nfunction getSelectedStyleName() {\n const styleValue = settings['段评样式'];\n \/\/ 如果是数字,返回默认样式名称\n if (!isNaN(styleValue)) {\n return STYLE_NAMES[parseInt(styleValue)] || styleValue;\n }\n \/\/ 如果是自定义样式,返回自定义名称\n return styleValue;\n}\n\n\/\/ 删除自定义样式\nfunction deleteCustomStyle(name) {\n if (confirm('确定删除 \"' + name + '\" 吗?')) {\n delete customStyles[name];\n \/\/ 如果删除的是当前选中的样式,切换到默认样式\n if (settings['段评样式'] === name) {\n settings['段评样式'] = '0';\n }\n renderCustomStyles();\n updateHiddenSettings();\n updateStyleSelector();\n renderPreview();\n }\n}\n\n\/\/ 更新自定义样式\nfunction updateCustomStyle(name, value) {\n customStyles[name] = value;\n updateHiddenSettings();\n renderPreview();\n}\n\n\/\/ 重命名自定义样式\nfunction renameCustomStyle(oldName, newName) {\n if (!newName || newName.trim() == '') return;\n if (customStyles[newName] && newName != oldName) {\n alert('名称已存在');\n return;\n }\n customStyles[newName] = customStyles[oldName];\n delete customStyles[oldName];\n \/\/ 如果当前选中的是旧名称,更新为新名称\n if (settings['段评样式'] === oldName) {\n settings['段评样式'] = newName;\n }\n renderCustomStyles();\n updateHiddenSettings();\n updateStyleSelector();\n renderPreview();\n}\n\n\/\/ 渲染自定义样式列表\nfunction renderCustomStyles() {\n const container = document.getElementById('customStylesContainer');\n container.innerHTML = '';\n \n if (Object.keys(customStyles).length === 0) {\n container.innerHTML = '<div class=\"empty-custom-styles\">暂无自定义样式,点击上方\"+\"添加<\/div>';\n return;\n }\n \n Object.keys(customStyles).forEach((name, index) => {\n const styleCard = document.createElement('div');\n styleCard.className = 'custom-style-card';\n \/\/ 转义SVG代码中的特殊字符\n const escapedSvg = customStyles[name]\n .replace(\/&\/g, '&')\n .replace(\/<\/g, '<')\n .replace(\/>\/g, '>')\n .replace(\/\"\/g, '"')\n .replace(\/'\/g, ''');\n \n styleCard.innerHTML = \\`\n <div class=\"custom-style-header\">\n <input type=\"text\" class=\"style-name-input\" value=\"\\${name}\" data-original=\"\\${name}\">\n <div class=\"style-actions\">\n <button class=\"style-btn preview-btn\" title=\"预览\">\n <i class=\"fas fa-eye\"><\/i>\n <\/button>\n <button class=\"style-btn delete-btn\" title=\"删除\">\n <i class=\"fas fa-trash\"><\/i>\n <\/button>\n <\/div>\n <\/div>\n <textarea class=\"style-code-textarea\" rows=\"4\" placeholder=\"输入SVG代码...\">\\${escapedSvg}<\/textarea>\n <div class=\"style-preview-small\">\n <div class=\"preview-svg-container\"><\/div>\n <\/div>\n \\`;\n \n \/\/ 绑定事件\n const nameInput = styleCard.querySelector('.style-name-input');\n const codeTextarea = styleCard.querySelector('.style-code-textarea');\n const deleteBtn = styleCard.querySelector('.delete-btn');\n const previewBtn = styleCard.querySelector('.preview-btn');\n \n nameInput.addEventListener('change', () => {\n const newName = nameInput.value.trim();\n const originalName = nameInput.dataset.original;\n if (newName != originalName) {\n renameCustomStyle(originalName, newName);\n }\n });\n \n codeTextarea.addEventListener('input', () => {\n updateCustomStyle(name, codeTextarea.value);\n \/\/ 更新小预览\n updateSmallPreview(styleCard.querySelector('.preview-svg-container'), codeTextarea.value);\n });\n \n deleteBtn.addEventListener('click', () => deleteCustomStyle(name));\n \n previewBtn.addEventListener('click', () => {\n \/\/ 设置为当前选中样式(使用自定义名称)\n settings['段评样式'] = name;\n document.getElementById('duanpingStyle').value = name;\n updateHiddenSettings();\n renderPreview();\n });\n \n \/\/ 初始化小预览\n updateSmallPreview(styleCard.querySelector('.preview-svg-container'), customStyles[name]);\n \n container.appendChild(styleCard);\n });\n}\n\n\/\/ 更新小预览\nfunction updateSmallPreview(container, svgCode) {\n try {\n const preview = svgCode\n .split('$color').join(settings['段评边框颜色'])\n .split('$fontcolor').join(settings['段评字体颜色'])\n .split('$tccolor').join(settings['段评填充颜色'])\n .split('$displayText').join('99+');\n container.innerHTML = preview;\n } catch (e) {\n container.innerHTML = '<span style=\"color: #999; font-size: 12px;\">解析失败<\/span>';\n }\n}\n\n\/\/ 渲染预览\nfunction renderPreview() {\n const container = document.getElementById('previewContainer');\n container.innerHTML = '';\n \n \/\/ 添加默认样式\n SVG_TEMPLATES.forEach((svgTemplate, index) => {\n const svgContent = svgTemplate\n .split('$color').join(settings['段评边框颜色'])\n .split('$fontcolor').join(settings['段评字体颜色'])\n .split('$tccolor').join(settings['段评填充颜色'])\n .split('$displayText').join('99+');\n \n const item = document.createElement('div');\n item.className = \\`preview-item \\${settings['段评样式'] === String(index) ? 'selected' : ''}\\`;\n item.innerHTML = \\`\n \\${svgContent}\n <div class=\"preview-label\">\\${STYLE_NAMES[index]}<\/div>\n \\`;\n \n item.addEventListener('click', () => {\n settings['段评样式'] = String(index);\n document.getElementById('duanpingStyle').value = String(index);\n updateHiddenSettings();\n renderPreview();\n });\n \n container.appendChild(item);\n });\n \n \/\/ 添加自定义样式预览\n Object.keys(customStyles).forEach((name) => {\n const svgCode = customStyles[name];\n let svgContent;\n try {\n svgContent = svgCode\n .split('$color').join(settings['段评边框颜色'])\n .split('$fontcolor').join(settings['段评字体颜色'])\n .split('$tccolor').join(settings['段评填充颜色'])\n .split('$displayText').join('99+');\n } catch (e) {\n svgContent = '<svg width=\"60\" height=\"54\" viewBox=\"0 0 60 54\"><text x=\"30\" y=\"27\" text-anchor=\"middle\" fill=\"#999\" font-size=\"10\">解析失败<\/text><\/svg>';\n }\n \n const item = document.createElement('div');\n item.className = \\`preview-item \\${settings['段评样式'] === name ? 'selected' : ''}\\`;\n item.innerHTML = \\`\n \\${svgContent}\n <div class=\"preview-label custom-label\">\\${name}<\/div>\n \\`;\n \n item.addEventListener('click', () => {\n settings['段评样式'] = name;\n document.getElementById('duanpingStyle').value = name;\n updateHiddenSettings();\n renderPreview();\n });\n \n container.appendChild(item);\n });\n}\n\n\/\/ 初始化事件监听\nfunction setupEventListeners() {\n \/\/ 段评开关\n document.getElementById('duanpingSwitch').addEventListener('change', (e) => {\n settings['段评开关'] = e.target.checked ? 'true' : 'false';\n updateHiddenSettings();\n });\n \n \/\/ 气泡增大开关\n document.getElementById('bubbleLargeSwitch').addEventListener('change', (e) => {\n settings['气泡增大'] = e.target.checked ? 'true' : 'false';\n updateHiddenSettings();\n });\n \n \/\/ 段评样式选择\n document.getElementById('duanpingStyle').addEventListener('change', (e) => {\n settings['段评样式'] = e.target.value;\n updateHiddenSettings();\n renderPreview();\n });\n \n \/\/ 边框颜色选择器\n document.getElementById('borderColor').addEventListener('input', (e) => {\n const color = e.target.value;\n settings['段评边框颜色'] = color;\n document.getElementById('borderColorText').value = color;\n updateHiddenSettings();\n renderPreview();\n });\n \n \/\/ 边框颜色文本输入\n document.getElementById('borderColorText').addEventListener('input', (e) => {\n const color = e.target.value;\n if (\/^#[0-9A-Fa-f]{6}$\/.test(color) || \/^#[0-9A-Fa-f]{3}$\/.test(color)) {\n settings['段评边框颜色'] = color;\n document.getElementById('borderColor').value = color;\n updateHiddenSettings();\n renderPreview();\n }\n });\n \n \/\/ 字体颜色选择器\n document.getElementById('fontColor').addEventListener('input', (e) => {\n const color = e.target.value;\n settings['段评字体颜色'] = color;\n document.getElementById('fontColorText').value = color;\n updateHiddenSettings();\n renderPreview();\n });\n \n \/\/ 字体颜色文本输入\n document.getElementById('fontColorText').addEventListener('input', (e) => {\n const color = e.target.value;\n if (\/^#[0-9A-Fa-f]{6}$\/.test(color) || \/^#[0-9A-Fa-f]{3}$\/.test(color)) {\n settings['段评字体颜色'] = color;\n document.getElementById('fontColor').value = color;\n updateHiddenSettings();\n renderPreview();\n }\n });\n \n \/\/ 填充颜色选择器\n document.getElementById('fillColor').addEventListener('input', (e) => {\n const color = e.target.value;\n settings['段评填充颜色'] = color;\n document.getElementById('fillColorText').value = color;\n updateHiddenSettings();\n renderPreview();\n });\n \n \/\/ 填充颜色文本输入\n document.getElementById('fillColorText').addEventListener('input', (e) => {\n const color = e.target.value;\n if (color.toLowerCase() == 'transparent' || \/^#[0-9A-Fa-f]{6}$\/.test(color) || \/^#[0-9A-Fa-f]{3}$\/.test(color)) {\n settings['段评填充颜色'] = color;\n if (color.toLowerCase() != 'transparent') {\n document.getElementById('fillColor').value = color;\n }\n updateHiddenSettings();\n renderPreview();\n }\n });\n \n \/\/ 透明按钮\n document.getElementById('fillColorTransparent').addEventListener('click', () => {\n settings['段评填充颜色'] = 'transparent';\n document.getElementById('fillColorText').value = 'transparent';\n document.getElementById('fillColor').value = '#FFFFFF';\n updateHiddenSettings();\n renderPreview();\n });\n \n \/\/ 添加自定义样式按钮\n document.getElementById('addCustomStyleBtn').addEventListener('click', addCustomStyle);\n}\n\n\/\/ 初始化UI\nfunction initUI() {\n \/\/ 设置段评开关\n document.getElementById('duanpingSwitch').checked = settings['段评开关'] != 'false';\n \n \/\/ 设置气泡增大开关\n document.getElementById('bubbleLargeSwitch').checked = settings['气泡增大'] == 'true';\n \n \/\/ 设置段评样式\n document.getElementById('duanpingStyle').value = settings['段评样式'];\n \n \/\/ 设置边框颜色\n const borderColorValue = settings['段评边框颜色'];\n document.getElementById('borderColor').value = borderColorValue === 'transparent' ? '#A7A7A7' : borderColorValue;\n document.getElementById('borderColorText').value = borderColorValue;\n \n \/\/ 设置字体颜色\n const fontColorValue = settings['段评字体颜色'];\n document.getElementById('fontColor').value = fontColorValue === 'transparent' ? '#A7A7A7' : fontColorValue;\n document.getElementById('fontColorText').value = fontColorValue;\n \n \/\/ 设置填充颜色\n const fillColorValue = settings['段评填充颜色'];\n document.getElementById('fillColor').value = fillColorValue === 'transparent' ? '#FFFFFF' : fillColorValue;\n document.getElementById('fillColorText').value = fillColorValue;\n \n \/\/ 渲染自定义样式列表\n renderCustomStyles();\n \n \/\/ 更新段评样式选择器,添加自定义样式选项\n updateStyleSelector();\n}\n\n\/\/ 更新段评样式选择器\nfunction updateStyleSelector() {\n const select = document.getElementById('duanpingStyle');\n const currentValue = settings['段评样式'];\n \n \/\/ 移除所有非默认选项\n while (select.options.length > 6) {\n select.remove(6);\n }\n \n \/\/ 添加自定义样式选项\n Object.keys(customStyles).forEach((name) => {\n const option = document.createElement('option');\n option.value = name;\n option.textContent = name;\n select.appendChild(option);\n });\n \n \/\/ 设置当前选中值\n select.value = currentValue;\n}\n\n\/\/ 页面加载完成后初始化\ninitCustomStyles();\ninitUI();\nsetupEventListeners();\nrenderPreview();\n<\/script>\n<\/body>\n<\/html>\n `;\n let body;\n try {\n body = java.startBrowserAwait(`data:text\/html;base64,${java.base64Encode(html)}`, '光遇段评设置', false).body();\n } catch (e) {\n if (islyc == \"安卓\") {\n java.longToast(\"\\n当前软件版本过低,请更新软件,或者使用底部备用设置方案~\");\n } else {\n java.longToast(\"\\n请点击页面右上角√才能设置成功哦~\");\n }\n return;\n }\n \n let settings = {};\n try {\n const getSpanText = (spanId) => {\n const regex = new RegExp('id=\"' + spanId + '\"\\s*>\\s*([^<]*?)\\s*<\\\/span>');\n const match = body.match(regex);\n return match ? match[1].trim() : \"\";\n };\n const getTextareaValue = (textareaId) => {\n const regex = new RegExp('<textarea[^>]*id=\"' + textareaId + '\"[^>]*>([\\\\s\\\\S]*?)<\\\/textarea>', 'i');\n const match = body.match(regex);\n return match ? match[1].trim() : \"\";\n };\n const htmlDecode = (str) => {\n if (!str) return str;\n return str.replace(\/&\/g, '&')\n .replace(\/<\/g, '<')\n .replace(\/>\/g, '>')\n .replace(\/"\/g, '\"')\n .replace(\/'\/g, \"'\");\n };\n\n const customStylesValue = getTextareaValue('customStylesValue') || '{}';\n const decodedStyles = htmlDecode(customStylesValue);\n\n settings = {\n '段评开关': getSpanText('duanpingSwitchValue'),\n '气泡增大': getSpanText('bubbleLargeSwitchValue'),\n '段评样式': getSpanText('duanpingStyleValue') || '0',\n '段评边框颜色': getSpanText('borderColorValue') || '#A7A7A7',\n '段评字体颜色': getSpanText('fontColorValue') || '#A7A7A7',\n '段评填充颜色': getSpanText('fillColorValue') || 'transparent',\n '自定义段评样式': JSON.parse(decodedStyles),\n };\n const settingsText = Object.keys(settings).map(key => `${key}: ${settings[key]}`).join('\\n');\n java.longToast(\"\\n\" + settingsText);\n } catch (e) {\n settings = {\n '段评开关': 'true',\n '气泡增大': 'false',\n '段评样式': '0',\n '段评边框颜色': '#A7A7A7',\n '段评字体颜色': '#A7A7A7',\n '段评填充颜色': 'transparent',\n '自定义段评样式': {},\n };\n }\n this.setVariable(\"段评设置\", settings, false);\n}\n\n\/\/ 线路设置\nfunction getServerSettings() {\n const {java} = this;\n let data = this.getVariable('线路');\n \/\/java.toast(data)\n let hostsbk = this.getVariable('云端配置')['hosts'] || hosts;\n \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,maximum-scale=1,user-scalable=no\">\n<title>光遇小说 - 线路设置<\/title>\n<link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.4.0\/css\/all.min.css\">\n<style>\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n -webkit-tap-highlight-color: transparent;\n}\n\n:root {\n --color-primary: #2D9D78;\n --color-primary-light: #3DB893;\n --bg-dark: #f5f7fa;\n --bg-darker: #ffffff;\n --bg-card: rgba(255, 255, 255, 0.95);\n --bg-elevated: #f8fafb;\n --text-primary: #1a1d23;\n --text-secondary: #6b7280;\n --border-color: rgba(0, 0, 0, 0.08);\n}\n\n[data-theme=\"dark\"] {\n --bg-dark: #0A1628;\n --bg-darker: #050B14;\n --bg-card: #142235;\n --bg-elevated: #1A2B42;\n --text-primary: #FFFFFF;\n --text-secondary: rgba(255, 255, 255, 0.7);\n --border-color: rgba(255, 255, 255, 0.1);\n}\n\nhtml, body {\n width: 100%;\n min-height: 100vh;\n font-family: -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 16px;\n color: var(--text-primary);\n background: var(--bg-darker);\n overflow-x: hidden;\n}\n\n.container {\n padding: 16px;\n max-width: 600px;\n margin: 0 auto;\n}\n\n.main-card {\n background: var(--bg-card);\n border-radius: 20px;\n box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1);\n border: 1px solid var(--border-color);\n overflow: hidden;\n}\n\n.header {\n padding: 20px 16px;\n background: linear-gradient(135deg, var(--color-primary), var(--color-primary-light));\n text-align: center;\n}\n\n.header-title {\n font-size: 18px;\n font-weight: 700;\n color: white;\n margin-bottom: 4px;\n}\n\n.header-desc {\n font-size: 12px;\n color: rgba(255, 255, 255, 0.8);\n}\n\n.last-check {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: var(--text-secondary);\n padding: 12px 16px;\n border-bottom: 1px solid var(--border-color);\n}\n\n.last-check i {\n color: var(--color-primary);\n}\n\n.server-list {\n padding: 8px;\n}\n\n.server-card {\n background: var(--bg-elevated);\n border-radius: 12px;\n padding: 14px 16px;\n margin-bottom: 8px;\n display: flex;\n align-items: center;\n gap: 12px;\n cursor: pointer;\n transition: all 0.2s ease;\n border: 2px solid transparent;\n}\n\n.server-card:hover {\n border-color: var(--color-primary);\n background: rgba(45, 157, 120, 0.05);\n}\n\n.server-card.selected {\n border-color: var(--color-primary);\n background: rgba(45, 157, 120, 0.1);\n}\n\n.server-icon {\n width: 36px;\n height: 36px;\n border-radius: 10px;\n background: linear-gradient(135deg, rgba(45, 157, 120, 0.15), rgba(61, 184, 147, 0.1));\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n color: var(--color-primary);\n flex-shrink: 0;\n}\n\n.server-card.selected .server-icon {\n background: linear-gradient(135deg, var(--color-primary), var(--color-primary-light));\n color: white;\n}\n\n.server-info {\n flex: 1;\n min-width: 0;\n}\n\n.server-url {\n font-size: 14px;\n font-weight: 700;\n color: var(--text-primary);\n word-break: break-all;\n line-height: 1.4;\n}\n\n.server-details {\n font-size: 11px;\n color: var(--text-secondary);\n margin-top: 2px;\n}\n\n.server-status {\n padding: 4px 12px;\n border-radius: 16px;\n font-size: 11px;\n font-weight: 700;\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.server-status.checking {\n background: rgba(255, 193, 7, 0.2);\n color: #f57f17;\n}\n\n.server-status.online {\n background: rgba(76, 175, 80, 0.2);\n color: #2e7d32;\n}\n\n.server-status.slow {\n background: rgba(255, 193, 7, 0.2);\n color: #f57f17;\n}\n\n.server-status.offline {\n background: rgba(244, 67, 54, 0.2);\n color: #c62828;\n}\n\n.server-status.not-support {\n background: rgba(156, 163, 175, 0.2);\n color: #6b7280;\n}\n\n.selected-badge {\n width: 20px;\n height: 20px;\n border-radius: 50%;\n background: var(--color-primary);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.selected-badge i {\n color: white;\n font-size: 12px;\n}\n\n.footer {\n padding: 16px;\n border-top: 1px solid var(--border-color);\n text-align: center;\n}\n\n.footer-text {\n font-size: 12px;\n color: var(--text-secondary);\n line-height: 1.5;\n}\n\n.footer-text .highlight {\n color: var(--color-primary);\n font-weight: 700;\n}\n\n\/* 自动切换开关样式 *\/\n.auto-switch-section {\n padding: 14px 16px;\n border-bottom: 1px solid var(--border-color);\n display: flex;\n align-items: center;\n justify-content: space-between;\n background: var(--bg-elevated);\n}\n\n.auto-switch-section:hover {\n background: rgba(45, 157, 120, 0.05);\n}\n\n.auto-switch-info {\n flex: 1;\n min-width: 0;\n}\n\n.auto-switch-title {\n font-size: 14px;\n font-weight: 600;\n color: var(--text-primary);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.auto-switch-title i {\n color: var(--color-primary);\n font-size: 14px;\n}\n\n.auto-switch-desc {\n font-size: 11px;\n color: var(--text-secondary);\n margin-top: 3px;\n line-height: 1.4;\n}\n\n\/* 开关基础样式 *\/\n.switch {\n position: relative;\n display: inline-block;\n width: 48px;\n height: 28px;\n flex-shrink: 0;\n}\n\n.switch input {\n opacity: 0;\n width: 0;\n height: 0;\n}\n\n.switch-slider {\n position: absolute;\n cursor: pointer;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: #d1d5db;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n border-radius: 28px;\n box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);\n}\n\n.switch-slider:before {\n position: absolute;\n content: \"\";\n height: 22px;\n width: 22px;\n left: 3px;\n bottom: 3px;\n background-color: white;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n border-radius: 50%;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n\n.switch input:checked + .switch-slider {\n background: linear-gradient(135deg, var(--color-primary), var(--color-primary-light));\n box-shadow: 0 2px 8px rgba(45, 157, 120, 0.3);\n}\n\n.switch input:checked + .switch-slider:before {\n transform: translateX(20px);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);\n}\n\n\/* 开关焦点状态 *\/\n.switch input:focus + .switch-slider {\n outline: 2px solid var(--color-primary);\n outline-offset: 2px;\n}\n\n\/* 下拉框样式 *\/\n.timeout-select {\n padding: 6px 12px;\n font-size: 13px;\n font-weight: 600;\n color: var(--text-primary);\n background: var(--bg-card);\n border: 2px solid var(--border-color);\n border-radius: 10px;\n cursor: pointer;\n min-width: 70px;\n appearance: none;\n background-image: url(\"data:image\/svg+xml,%3Csvg xmlns='http:\/\/www.w3.org\/2000\/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C\/polyline%3E%3C\/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 8px center;\n background-size: 14px;\n}\n\n.timeout-select:focus {\n outline: none;\n border-color: var(--color-primary);\n}\n\n.timeout-select option {\n background: var(--bg-darker);\n color: var(--text-primary);\n}\n\n.hidden-settings {\n display: none;\n}\/* 提示文案样式 *\/\n.tip-alert {\n background: linear-gradient(135deg, rgba(255, 193, 7, 0.12) 0%, rgba(255, 152, 0, 0.08) 100%);\n border: 1px solid rgba(255, 193, 7, 0.25);\n border-radius: 16px;\n padding: 16px 18px;\n margin: 20px;\n display: flex;\n align-items: flex-start;\n gap: 14px;\n box-shadow: 0 4px 16px rgba(255, 193, 7, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.8);\n position: relative;\n overflow: hidden;\n}\n\n.tip-alert::before {\n content: '';\n position: absolute;\n top: -50%;\n right: -20%;\n width: 100px;\n height: 100px;\n background: rgba(255, 193, 7, 0.1);\n border-radius: 50%;\n}\n\n.tip-alert i {\n color: #f97316;\n font-size: 22px;\n flex-shrink: 0;\n margin-top: 2px;\n position: relative;\n z-index: 1;\n}\n\n.tip-alert p {\n font-size: 13px;\n color: var(--text-secondary);\n line-height: 1.6;\n margin: 0;\n position: relative;\n z-index: 1;\n}\n\n.tip-alert .highlight-text {\n color: #ea580c;\n font-weight: 700;\n}\n\n@media (max-width: 600px) {\n .tip-alert {\n margin: 16px;\n padding: 14px 16px;\n }\n}\n<\/style>\n<\/head>\n<body data-theme=\"light\">\n<div class=\"container\">\n <div class=\"main-card\">\n <div class=\"header\">\n <div class=\"header-title\">\n <i class=\"fas fa-server\"><\/i>\n <span style=\"margin-left: 8px;\">线路选择<\/span>\n <\/div>\n <div class=\"header-desc\">选择最快的服务器线路以获得最佳体验<\/div>\n <\/div>\n \n <!-- 提示文案 -->\n <div class=\"tip-alert\">\n <p>设置完成后请点击右上角的<span class=\"highlight-text\">√<\/span>应用设置<\/p>\n <\/div>\n \n <div class=\"footer\">\n <div class=\"footer-text\">\n 当前选中: <span class=\"highlight\" id=\"currentServer\">--<\/span>\n <\/div>\n <\/div>\n \n <div class=\"last-check\">\n <i class=\"fas fa-clock\"><\/i>\n <span id=\"lastCheckTime\">最后检测: --:--:--<\/span>\n <button id=\"refreshBtn\" style=\"margin-left: auto; padding: 4px 10px; font-size: 11px; font-weight: 600; color: var(--color-primary); background: rgba(45, 157, 120, 0.1); border: 1px solid var(--color-primary); border-radius: 6px; cursor: pointer;\">\n <i class=\"fas fa-sync-alt\"><\/i>\n 刷新\n <\/button>\n <\/div>\n \n <div class=\"auto-switch-section\">\n <div class=\"auto-switch-info\">\n <div class=\"auto-switch-title\"><i class=\"fas fa-refresh\"><\/i>失败自动切换<\/div>\n <div class=\"auto-switch-desc\">当前线路失败时自动尝试切换到其他可用线路<\/div>\n <\/div>\n <label class=\"switch\">\n <input type=\"checkbox\" id=\"autoSwitchToggle\">\n <span class=\"switch-slider\"><\/span>\n <\/label>\n <\/div>\n \n <div class=\"auto-switch-section\">\n <div class=\"auto-switch-info\">\n <div class=\"auto-switch-title\"><i class=\"fas fa-clock\"><\/i>超时自动切换<\/div>\n <div class=\"auto-switch-desc\">请求超时后自动切换到其他可用线路<\/div>\n <\/div>\n <div style=\"display: flex; align-items: center; gap: 12px;\">\n <label class=\"switch\">\n <input type=\"checkbox\" id=\"timeoutSwitchToggle\">\n <span class=\"switch-slider\"><\/span>\n <\/label>\n <select id=\"timeoutSelect\" class=\"timeout-select\">\n <option value=\"1\">1秒<\/option>\n <option value=\"2\">2秒<\/option>\n <option value=\"3\">3秒<\/option>\n <option value=\"4\">4秒<\/option>\n <option value=\"5\" selected>5秒<\/option>\n <option value=\"6\">6秒<\/option>\n <option value=\"7\">7秒<\/option>\n <option value=\"8\">8秒<\/option>\n <option value=\"9\">9秒<\/option>\n <option value=\"10\">10秒<\/option>\n <\/select>\n <\/div>\n <\/div>\n \n <div class=\"auto-switch-section\">\n <div class=\"auto-switch-info\">\n <div class=\"auto-switch-title\"><i class=\"fas fa-bell\"><\/i>切换提醒弹窗<\/div>\n <div class=\"auto-switch-desc\">线路切换时显示提醒弹窗通知<\/div>\n <\/div>\n <label class=\"switch\">\n <input type=\"checkbox\" id=\"notifySwitchToggle\">\n <span class=\"switch-slider\"><\/span>\n <\/label>\n <\/div>\n \n <div id=\"serverList\" class=\"server-list\"><\/div>\n \n <div class=\"hidden-settings\">\n <span id=\"serverValue\">${data || hostsbk[0]}<\/span>\n <span id=\"autoSwitchValue\">${this.getVariable('自动切换') || 'true'}<\/span>\n <span id=\"timeoutSwitchValue\">${this.getVariable('超时自动切换') || 'true'}<\/span>\n <span id=\"timeoutValue\">${this.getVariable('超时时间') || '5'}<\/span>\n <span id=\"notifySwitchValue\">${this.getVariable('切换提醒') || 'true'}<\/span>\n <\/div>\n <\/div>\n<\/div>\n\n<script>\nconst HOSTS = ${JSON.stringify(hostsbk)};\nlet currentServer = '${data || hostsbk[0]}';\n\nfunction renderServerList() {\n const serverList = document.getElementById('serverList');\n serverList.innerHTML = '';\n \n HOSTS.forEach((host, index) => {\n const card = document.createElement('div');\n card.className = 'server-card' + (currentServer === host ? ' selected' : '');\n card.dataset.host = host;\n card.dataset.index = index;\n \n card.innerHTML = \\`\n <div class=\"server-icon\">\n <i class=\"fas fa-server\"><\/i>\n <\/div>\n <div class=\"server-info\">\n <div class=\"server-url\">\\${host}<\/div>\n <div class=\"server-details\" id=\"details-\\${index}\">状态: 检测中<\/div>\n <\/div>\n <span class=\"server-status checking\" id=\"status-\\${index}\">\n <i class=\"fas fa-spinner fa-spin\"><\/i>\n <span>检测中<\/span>\n <\/span>\n \\${currentServer === host ? '<div class=\"selected-badge\"><i class=\"fas fa-check\"><\/i><\/div>' : ''}\n \\`;\n \n card.addEventListener('click', () => {\n selectServer(host);\n });\n \n serverList.appendChild(card);\n });\n}\n\nfunction selectServer(host) {\n currentServer = host;\n \n document.querySelectorAll('.server-card').forEach(card => {\n card.classList.remove('selected');\n const badge = card.querySelector('.selected-badge');\n if (badge) badge.remove();\n });\n \n const selectedCard = document.querySelector(\\`[data-host=\"\\${host}\"]\\`);\n if (selectedCard) {\n selectedCard.classList.add('selected');\n const icon = selectedCard.querySelector('.server-icon');\n icon.style.background = 'linear-gradient(135deg, var(--color-primary), var(--color-primary-light))';\n icon.style.color = 'white';\n \n const statusEl = selectedCard.querySelector('.server-status');\n const newBadge = document.createElement('div');\n newBadge.className = 'selected-badge';\n newBadge.innerHTML = '<i class=\"fas fa-check\"><\/i>';\n selectedCard.appendChild(newBadge);\n }\n \n document.getElementById('currentServer').textContent = host;\n document.getElementById('serverValue').textContent = host;\n}\n\nasync function checkSingleServer(host, index) {\n const currentProtocol = window.location.protocol;\n const statusEl = document.getElementById(\\`status-\\${index}\\`);\n const detailsEl = document.getElementById(\\`details-\\${index}\\`);\n \n try {\n const serverProtocol = new URL(host).protocol;\n if (currentProtocol === 'https:' && serverProtocol === 'http:') {\n statusEl.className = 'server-status not-support';\n statusEl.innerHTML = '<i class=\"fas fa-exclamation-triangle\"><\/i><span>不支持<\/span>';\n detailsEl.textContent = '浏览器安全策略限制';\n return;\n }\n \n const startTime = performance.now();\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 5000);\n \n const response = await fetch(\\`\\${host}\/health?t=\\${Date.now()}\\`, {\n signal: controller.signal,\n method: 'GET',\n mode: 'cors',\n cache: 'no-cache'\n });\n \n clearTimeout(timeout);\n const responseTime = Math.round(performance.now() - startTime);\n \n if (response.ok) {\n if (responseTime < 1000) {\n statusEl.className = 'server-status online';\n statusEl.innerHTML = \\`<i class=\"fas fa-check-circle\"><\/i><span>\\${responseTime}ms<\/span>\\`;\n } else if (responseTime < 2000) {\n statusEl.className = 'server-status slow';\n statusEl.innerHTML = \\`<i class=\"fas fa-exclamation-circle\"><\/i><span>\\${responseTime}ms<\/span>\\`;\n } else {\n statusEl.className = 'server-status slow';\n statusEl.innerHTML = '<i class=\"fas fa-exclamation-circle\"><\/i><span>缓慢<\/span>';\n }\n detailsEl.textContent = '运行正常';\n } else {\n statusEl.className = 'server-status offline';\n statusEl.innerHTML = '<i class=\"fas fa-times-circle\"><\/i><span>异常<\/span>';\n detailsEl.textContent = 'HTTP状态码: ' + response.status;\n }\n } catch (error) {\n statusEl.className = 'server-status offline';\n statusEl.innerHTML = '<i class=\"fas fa-times-circle\"><\/i><span>离线<\/span>';\n detailsEl.textContent = '连接失败';\n }\n}\n\nasync function checkServers() {\n HOSTS.forEach((_, index) => {\n const statusEl = document.getElementById(\\`status-\\${index}\\`);\n const detailsEl = document.getElementById(\\`details-\\${index}\\`);\n if (statusEl && detailsEl) {\n statusEl.className = 'server-status checking';\n statusEl.innerHTML = '<i class=\"fas fa-spinner fa-spin\"><\/i><span>检测中<\/span>';\n detailsEl.textContent = '状态: 检测中';\n }\n });\n \n const promises = HOSTS.map((host, index) => checkSingleServer(host, index));\n await Promise.all(promises);\n \n const now = new Date();\n document.getElementById('lastCheckTime').textContent = \\`最后检测: \\${now.toLocaleTimeString()}\\`;\n}\n\ndocument.getElementById('refreshBtn').addEventListener('click', checkServers);\n\ndocument.addEventListener('DOMContentLoaded', () => {\n renderServerList();\n document.getElementById('currentServer').textContent = currentServer;\n checkServers();\n \n \/\/ 初始化自动切换开关\n const autoSwitchToggle = document.getElementById('autoSwitchToggle');\n const autoSwitchValue = document.getElementById('autoSwitchValue').textContent;\n autoSwitchToggle.checked = autoSwitchValue !== 'false';\n \n \/\/ 监听开关变化\n autoSwitchToggle.addEventListener('change', (e) => {\n document.getElementById('autoSwitchValue').textContent = e.target.checked ? 'true' : 'false';\n });\n \n \/\/ 初始化超时自动切换开关\n const timeoutSwitchToggle = document.getElementById('timeoutSwitchToggle');\n const timeoutSwitchValue = document.getElementById('timeoutSwitchValue').textContent;\n timeoutSwitchToggle.checked = timeoutSwitchValue !== 'false';\n \n \/\/ 监听超时自动切换开关变化\n timeoutSwitchToggle.addEventListener('change', (e) => {\n document.getElementById('timeoutSwitchValue').textContent = e.target.checked ? 'true' : 'false';\n });\n \n \/\/ 初始化超时时间下拉框\n const timeoutSelect = document.getElementById('timeoutSelect');\n const timeoutValue = document.getElementById('timeoutValue').textContent;\n timeoutSelect.value = timeoutValue;\n \n \/\/ 监听超时时间变化\n timeoutSelect.addEventListener('change', (e) => {\n document.getElementById('timeoutValue').textContent = e.target.value;\n });\n \n \/\/ 初始化切换提醒弹窗开关\n const notifySwitchToggle = document.getElementById('notifySwitchToggle');\n const notifySwitchValue = document.getElementById('notifySwitchValue').textContent;\n notifySwitchToggle.checked = notifySwitchValue !== 'false';\n \n \/\/ 监听切换提醒弹窗开关变化\n notifySwitchToggle.addEventListener('change', (e) => {\n document.getElementById('notifySwitchValue').textContent = e.target.checked ? 'true' : 'false';\n });\n});\n<\/script>\n<\/body>\n<\/html>\n `;\n let body;\n try {\n body = java.startBrowserAwait(`data:text\/html;base64,${java.base64Encode(html)}`, '光遇服务器设置', false).body();\n } catch (e) {\n let islyc = this.checkEnv()\n if (islyc == \"安卓\") {\n this.switchToNextLine();\n } else {\n java.longToast(\"\\n请点击页面右上角√才能设置成功哦~\");\n }\n return;\n }\n \n let host = hostsbk[0];\n let autoSwitch = 'true';\n let timeoutSwitch = 'true';\n let timeout = '5';\n let notifySwitch = 'true';\n try {\n const getSpanText = (spanId) => {\n const regex = new RegExp('id=\"' + spanId + '\"\\s*>\\s*([^<]*?)\\s*<\\\/span>');\n const match = body.match(regex);\n return match ? match[1].trim() : \"\";\n };\n host = getSpanText('serverValue') || hostsbk[0];\n autoSwitch = getSpanText('autoSwitchValue') || 'true';\n timeoutSwitch = getSpanText('timeoutSwitchValue') || 'true';\n timeout = getSpanText('timeoutValue') || '5';\n notifySwitch = getSpanText('notifySwitchValue') || 'true';\n } catch (e) {\n host = hostsbk[0];\n autoSwitch = 'true';\n timeoutSwitch = 'true';\n timeout = '5';\n notifySwitch = 'true';\n }\n const settingsText = `线路: ${host}\\n失败自动切换: ${autoSwitch == 'true' ? '开启' : '关闭'}\\n超时自动切换: ${timeoutSwitch == 'true' ? '开启' : '关闭'}\\n超时时间: ${timeout}秒\\n切换提醒弹窗: ${notifySwitch == 'true' ? '开启' : '关闭'}`;\n java.longToast(\"\\n\" + settingsText);\n this.setVariable(\"线路\", host, false);\n this.setVariable(\"自动切换\", autoSwitch, false);\n this.setVariable(\"超时自动切换\", timeoutSwitch, false);\n this.setVariable(\"超时时间\", timeout, false);\n this.setVariable(\"切换提醒\", notifySwitch, false);\n}\n\n\n\/\/ 更多设置\nfunction getHtmlSettings() {\n const {\n java\n } = this;\n let data = this.getVariable('更多设置');\n let config = this.getVariable('云端配置');\n if (!config) {\n try {\n this.getCloudSettings();\n config = this.getVariable('云端配置');\n } catch {}\n }\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,maximum-scale=1,user-scalable=no\">\n<title>光遇小说 - 管理中心<\/title>\n<link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.4.0\/css\/all.min.css\">\n<style>\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n -webkit-tap-highlight-color: transparent;\n}\n\n:root {\n --color-primary: #2D9D78;\n --color-primary-light: #3DB893;\n --color-primary-dark: #1E6F55;\n --color-accent: #FFB74D;\n --color-success: #4CAF50;\n --color-error: #F44336;\n --color-warning: #FFC107;\n --color-info: #2196F3;\n \n --bg-dark: #f0f3f8;\n --bg-darker: #e8edf5;\n --bg-card: rgba(255, 255, 255, 0.98);\n --bg-elevated: #fafbfc;\n \n --text-primary: #1a1d23;\n --text-secondary: #6b7280;\n --text-tertiary: #9ca3af;\n \n --border-color: rgba(0, 0, 0, 0.06);\n}\n\n[data-theme=\"dark\"] {\n --bg-dark: #0A1628;\n --bg-darker: #050B14;\n --bg-card: #142235;\n --bg-elevated: #1A2B42;\n \n --text-primary: #FFFFFF;\n --text-secondary: rgba(255, 255, 255, 0.7);\n --text-tertiary: rgba(255, 255, 255, 0.5);\n \n --border-color: rgba(255, 255, 255, 0.08);\n}\n\nhtml, body {\n width: 100%;\n min-height: 100vh;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 16px;\n color: var(--text-primary);\n background: linear-gradient(135deg, var(--bg-darker) 0%, var(--bg-dark) 100%);\n background-attachment: fixed;\n overflow-x: hidden;\n transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.container {\n padding: 24px 20px;\n max-width: 820px;\n margin: 0 auto;\n animation: fadeIn 0.5s ease-out;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; transform: translateY(10px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.main-card {\n background: var(--bg-card);\n border-radius: 24px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08), 0 2px 8px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.8);\n border: 1px solid var(--border-color);\n overflow: hidden;\n backdrop-filter: blur(20px);\n}\n\n\/* 主Tab切换 *\/\n.main-tabs {\n display: flex;\n background: linear-gradient(180deg, var(--bg-elevated) 0%, rgba(255, 255, 255, 0.5) 100%);\n border-bottom: 1px solid var(--border-color);\n position: relative;\n}\n\n.main-tabs::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 1px;\n background: linear-gradient(90deg, transparent, var(--border-color), transparent);\n}\n\n.main-tab {\n flex: 1;\n padding: 20px 24px;\n text-align: center;\n font-size: 15px;\n font-weight: 700;\n color: var(--text-secondary);\n cursor: pointer;\n transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);\n border-bottom: 3px solid transparent;\n position: relative;\n overflow: hidden;\n}\n\n.main-tab::before {\n content: '';\n position: absolute;\n top: 0;\n left: 50%;\n transform: translateX(-50%) scaleX(0);\n width: 60%;\n height: 3px;\n background: linear-gradient(90deg, var(--color-primary), var(--color-primary-light));\n transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);\n border-radius: 0 0 3px 3px;\n}\n\n.main-tab.active {\n color: var(--color-primary);\n background: linear-gradient(180deg, rgba(45, 157, 120, 0.06) 0%, transparent 100%);\n}\n\n.main-tab.active::before {\n transform: translateX(-50%) scaleX(1);\n}\n\n.main-tab:hover {\n color: var(--color-primary);\n background: rgba(45, 157, 120, 0.04);\n}\n\n.tab-content {\n padding: 20px;\n display: none;\n animation: slideIn 0.3s ease-out;\n}\n\n.tab-content.active {\n display: block;\n}\n\n@keyframes slideIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n\/* 来源选择子Tab *\/\n.source-subtabs {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 10px;\n margin-bottom: 20px;\n}\n\n.source-subtab {\n padding: 14px 10px;\n text-align: center;\n font-size: 12px;\n font-weight: 600;\n color: var(--text-secondary);\n background: var(--bg-elevated);\n border-radius: 14px;\n cursor: pointer;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n border: 2px solid transparent;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n}\n\n.source-subtab i {\n font-size: 18px;\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.source-subtab.novel.active {\n color: white;\n background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);\n border-color: rgba(239, 68, 68, 0.5);\n box-shadow: 0 8px 24px rgba(239, 68, 68, 0.35);\n transform: translateY(-2px);\n}\n\n.source-subtab.audio.active {\n color: white;\n background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);\n border-color: rgba(59, 130, 246, 0.5);\n box-shadow: 0 8px 24px rgba(59, 130, 246, 0.35);\n transform: translateY(-2px);\n}\n\n.source-subtab.comic.active {\n color: white;\n background: linear-gradient(135deg, #a855f7 0%, #9333ea 100%);\n border-color: rgba(168, 85, 247, 0.5);\n box-shadow: 0 8px 24px rgba(168, 85, 247, 0.35);\n transform: translateY(-2px);\n}\n\n.source-subtab.drama.active {\n color: white;\n background: linear-gradient(135deg, #f97316 0%, #ea580c 100%);\n border-color: rgba(249, 115, 22, 0.5);\n box-shadow: 0 8px 24px rgba(249, 115, 22, 0.35);\n transform: translateY(-2px);\n}\n\n.source-subtab:hover:not(.active) {\n border-color: var(--color-primary);\n background: rgba(45, 157, 120, 0.06);\n transform: translateY(-1px);\n}\n\n.source-subtab.active i {\n transform: scale(1.1);\n}\n\n\/* 来源选项 *\/\n.source-options {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(125px, 1fr));\n gap: 12px;\n}\n\n.source-option {\n padding: 14px 16px;\n background: var(--bg-elevated);\n border-radius: 12px;\n text-align: center;\n font-size: 13px;\n font-weight: 600;\n color: var(--text-secondary);\n cursor: pointer;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n border: 2px solid transparent;\n}\n\n.source-option:hover {\n border-color: var(--color-primary);\n background: rgba(45, 157, 120, 0.05);\n transform: translateY(-2px);\n}\n\n.source-option.selected {\n background: linear-gradient(135deg, rgba(45, 157, 120, 0.18), rgba(61, 184, 147, 0.08));\n color: var(--color-primary);\n border-color: var(--color-primary);\n box-shadow: 0 4px 16px rgba(45, 157, 120, 0.2);\n transform: translateY(-2px);\n}\n\n.source-option.all {\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n box-shadow: 0 4px 16px rgba(102, 126, 234, 0.3);\n}\n\n.source-option.all.selected {\n background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);\n box-shadow: 0 6px 24px rgba(102, 126, 234, 0.45);\n transform: translateY(-3px);\n}\n\n.source-option.all:hover {\n border-color: transparent;\n transform: translateY(-3px);\n}\n\n\/* 选中状态提示 *\/\n.selected-info {\n margin-top: 20px;\n padding: 16px 18px;\n background: var(--bg-elevated);\n border-radius: 12px;\n font-size: 13px;\n color: var(--text-secondary);\n border: 1px solid var(--border-color);\n}\n\n.selected-info span {\n color: var(--color-primary);\n font-weight: 700;\n}\n\n\/* 设置项样式 *\/\n.setting-item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px;\n background: var(--bg-elevated);\n border-radius: 16px;\n margin-bottom: 12px;\n border: 1px solid var(--border-color);\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n position: relative;\n overflow: hidden;\n}\n\n\n.setting-item:hover::before {\n opacity: 1;\n}\n\n.setting-info {\n flex: 1;\n min-width: 0;\n}\n\n.setting-title {\n font-size: 16px;\n font-weight: 700;\n color: var(--text-primary);\n margin-bottom: 6px;\n}\n\n.setting-desc {\n font-size: 13px;\n color: var(--text-secondary);\n line-height: 1.5;\n}\n\n\/* 开关样式 *\/\n.switch {\n position: relative;\n width: 54px;\n height: 30px;\n flex-shrink: 0;\n}\n\n.switch input {\n opacity: 0;\n width: 0;\n height: 0;\n}\n\n.slider {\n position: absolute;\n cursor: pointer;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: linear-gradient(135deg, #e5e7eb 0%, #d1d5db 100%);\n transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);\n border-radius: 30px;\n box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1), inset 0 -2px 4px rgba(0, 0, 0, 0.05);\n}\n\n.slider:before {\n position: absolute;\n content: \"\";\n height: 24px;\n width: 24px;\n left: 3px;\n bottom: 3px;\n background: linear-gradient(135deg, #ffffff 0%, #f3f4f6 100%);\n transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);\n border-radius: 50%;\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n\ninput:checked + .slider {\n background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-light) 100%);\n box-shadow: 0 4px 16px rgba(45, 157, 120, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.2);\n}\n\ninput:checked + .slider:before {\n transform: translateX(24px);\n background: linear-gradient(135deg, #ffffff 0%, #fefefe 100%);\n box-shadow: 0 3px 8px rgba(0, 0, 0, 0.2), 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n\n\/* 关键词高亮样式 *\/\n.highlight {\n color: var(--color-primary);\n font-weight: 700;\n position: relative;\n}\n\n.highlight::after {\n content: '';\n position: absolute;\n bottom: -2px;\n left: 0;\n width: 100%;\n height: 2px;\n background: linear-gradient(90deg, var(--color-primary), var(--color-primary-light));\n border-radius: 2px;\n}\n\n.highlight-orange {\n color: #f97316;\n font-weight: 700;\n}\n\n.highlight-blue {\n color: #3b82f6;\n font-weight: 700;\n}\n\n.highlight-purple {\n color: #a855f7;\n font-weight: 700;\n}\n\n.highlight-red {\n color: #ef4444;\n font-weight: 700;\n}\n\n\/* 选择器样式 *\/\n.select-wrapper {\n position: relative;\n flex-shrink: 0;\n}\n\n.select-wrapper select {\n appearance: none;\n -webkit-appearance: none;\n padding: 10px 34px 10px 14px;\n font-size: 13px;\n font-weight: 600;\n color: var(--text-primary);\n background: var(--bg-card);\n border: 2px solid var(--border-color);\n border-radius: 10px;\n cursor: pointer;\n min-width: 110px;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.select-wrapper select:hover {\n border-color: var(--color-primary);\n}\n\n.select-wrapper select:focus {\n outline: none;\n border-color: var(--color-primary);\n box-shadow: 0 0 0 3px rgba(45, 157, 120, 0.1);\n}\n\n.select-wrapper::after {\n content: '\\\\f078';\n font-family: 'Font Awesome 6 Free';\n font-weight: 900;\n position: absolute;\n right: 10px;\n top: 50%;\n transform: translateY(-50%);\n color: var(--text-secondary);\n pointer-events: none;\n font-size: 13px;\n transition: transform 0.3s ease;\n}\n\n.select-wrapper:hover::after {\n transform: translateY(-50%) rotate(180deg);\n}\n\n\/* 提示文案样式 *\/\n.tip-alert {\n background: linear-gradient(135deg, rgba(255, 193, 7, 0.12) 0%, rgba(255, 152, 0, 0.08) 100%);\n border: 1px solid rgba(255, 193, 7, 0.25);\n border-radius: 16px;\n padding: 16px 18px;\n margin: 20px;\n display: flex;\n align-items: flex-start;\n gap: 14px;\n box-shadow: 0 4px 16px rgba(255, 193, 7, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.8);\n position: relative;\n overflow: hidden;\n}\n\n.tip-alert::before {\n content: '';\n position: absolute;\n top: -50%;\n right: -20%;\n width: 100px;\n height: 100px;\n background: rgba(255, 193, 7, 0.1);\n border-radius: 50%;\n}\n\n.tip-alert i {\n color: #f97316;\n font-size: 22px;\n flex-shrink: 0;\n margin-top: 2px;\n position: relative;\n z-index: 1;\n}\n\n.tip-alert p {\n font-size: 13px;\n color: var(--text-secondary);\n line-height: 1.6;\n margin: 0;\n position: relative;\n z-index: 1;\n}\n\n.tip-alert .highlight-text {\n color: #ea580c;\n font-weight: 700;\n}\n\n\/* 响应式 *\/\n@media (max-width: 600px) {\n .container {\n padding: 16px 12px;\n }\n \n .main-card {\n border-radius: 20px;\n }\n \n .tab-content {\n padding: 16px;\n }\n \n .tip-alert {\n margin: 16px;\n padding: 14px 16px;\n }\n \n .source-subtabs {\n grid-template-columns: repeat(2, 1fr);\n gap: 8px;\n }\n \n .source-options {\n grid-template-columns: repeat(auto-fill, minmax(105px, 1fr));\n gap: 10px;\n }\n \n .setting-item {\n padding: 16px;\n }\n \n .setting-title {\n font-size: 15px;\n }\n \n .setting-desc {\n font-size: 12px;\n }\n}\n\n::-webkit-scrollbar {\n width: 6px;\n height: 6px;\n}\n\n::-webkit-scrollbar-track {\n background: var(--bg-elevated);\n border-radius: 3px;\n}\n\n::-webkit-scrollbar-thumb {\n background: var(--text-tertiary);\n border-radius: 3px;\n}\n\n::-webkit-scrollbar-thumb:hover {\n background: var(--text-secondary);\n}\n<\/style>\n<\/head>\n<body data-theme=\"light\">\n<div class=\"container\">\n <div class=\"main-card\">\n <div class=\"tip-alert\">\n <p>设置完成后请点击右上角的<span class=\"highlight-text\">√<\/span>应用设置<\/p>\n <\/div>\n \n <div class=\"main-tabs\">\n <div class=\"main-tab active\" id=\"tabBookSource\">书源设置<\/div>\n <div class=\"main-tab\" id=\"tabSource\">搜索设置<\/div>\n <\/div>\n \n <div class=\"tab-content active\" id=\"contentBookSource\">\n <div class=\"setting-item\">\n <div class=\"setting-info\">\n <div class=\"setting-title\">搜索模式<\/div>\n <div class=\"setting-desc\">切换后会搜索对应的类型,不推荐手动设置,可以在搜索的时候使用关键词:<span class=\"highlight-orange\">t:书名<\/span> 搜听书,<span class=\"highlight-blue\">d:书名<\/span> 搜短剧,<span class=\"highlight-purple\">m:书名<\/span> 搜漫画<\/div>\n <\/div>\n <div class=\"select-wrapper\">\n <select id=\"searchMode\">\n <option value=\"小说\">小说<\/option>\n <option value=\"听书\">听书<\/option>\n <option value=\"短剧\">短剧<\/option>\n <option value=\"漫画\">漫画<\/option>\n <\/select>\n <\/div>\n <\/div>\n \n <div class=\"setting-item\">\n <div class=\"setting-info\">\n <div class=\"setting-title\">显示图片<\/div>\n <div class=\"setting-desc\">关闭后,文章内的图片将会清除<\/div>\n <\/div>\n <label class=\"switch\">\n <input type=\"checkbox\" id=\"showImageSwitch\" checked>\n <span class=\"slider\"><\/span>\n <\/label>\n <\/div>\n \n <div class=\"setting-item\">\n <div class=\"setting-info\">\n <div class=\"setting-title\">完整简介<\/div>\n <div class=\"setting-desc\">关闭后,书籍详情页会精简简介内容<\/div>\n <\/div>\n <label class=\"switch\">\n <input type=\"checkbox\" id=\"fullDescSwitch\" checked>\n <span class=\"slider\"><\/span>\n <\/label>\n <\/div>\n \n <div class=\"setting-item\">\n <div class=\"setting-info\">\n <div class=\"setting-title\">同步书架<\/div>\n <div class=\"setting-desc\">开启后,刷新书籍详情页会将书架书籍同步到光遇网站网页端书架<\/div>\n <\/div>\n <label class=\"switch\">\n <input type=\"checkbox\" id=\"syncBookshelfSwitch\">\n <span class=\"slider\"><\/span>\n <\/label>\n <\/div>\n \n <div class=\"setting-item\" id=\"syncPopupSection\" style=\"display: none;\">\n <div class=\"setting-info\">\n <div class=\"setting-title\">同步弹窗<\/div>\n <div class=\"setting-desc\">弹窗开关可以让您知晓同步状态,关闭后不会弹窗<\/div>\n <\/div>\n <label class=\"switch\">\n <input type=\"checkbox\" id=\"syncPopupSwitch\">\n <span class=\"slider\"><\/span>\n <\/label>\n <\/div>\n \n <div class=\"setting-item\">\n <div class=\"setting-info\">\n <div class=\"setting-title\">网络模式<\/div>\n <div class=\"setting-desc\">开启后部分来源会使用本地网络请求,如<span class=\"highlight-orange\">69书吧<\/span>,可能需要开梯子访问,不推荐开启<\/div>\n <\/div>\n <div class=\"select-wrapper\">\n <select id=\"networkMode\">\n <option value=\"服务器\">服务器<\/option>\n <option value=\"本地\">本地<\/option>\n <\/select>\n <\/div>\n <\/div>\n \n <div class=\"setting-item\">\n <div class=\"setting-info\">\n <div class=\"setting-title\">强制搜索<\/div>\n <div class=\"setting-desc\">开启后搜索时会 <span class=\"highlight-red\">强制搜索全部来源<\/span>,搜索时长会 <span class=\"highlight-orange\">明显增长<\/span>,若无必要不建议开启<\/div>\n <\/div>\n <label class=\"switch\">\n <input type=\"checkbox\" id=\"forceSearchSwitch\">\n <span class=\"slider\"><\/span>\n <\/label>\n <\/div>\n \n <div class=\"setting-item\">\n <div class=\"setting-info\">\n <div class=\"setting-title\">目录显示来源<\/div>\n <div class=\"setting-desc\">开启后,目录最后一个章节的标题将显示来源,方便换源的时候开启加载目录可以知道是哪个来源<\/div>\n <\/div>\n <label class=\"switch\">\n <input type=\"checkbox\" id=\"showSourceInTocSwitch\" checked>\n <span class=\"slider\"><\/span>\n <\/label>\n <\/div>\n <\/div>\n \n <div class=\"tab-content\" id=\"contentSource\">\n <div class=\"source-subtabs\">\n <div class=\"source-subtab novel active\" id=\"subtabNovel\" data-type=\"小说\">\n <i class=\"fas fa-book-open\"><\/i>\n <span>小说<\/span>\n <\/div>\n <div class=\"source-subtab audio\" id=\"subtabAudio\" data-type=\"听书\">\n <i class=\"fas fa-headphones\"><\/i>\n <span>听书<\/span>\n <\/div>\n <div class=\"source-subtab comic\" id=\"subtabComic\" data-type=\"漫画\">\n <i class=\"fas fa-image\"><\/i>\n <span>漫画<\/span>\n <\/div>\n <div class=\"source-subtab drama\" id=\"subtabDrama\" data-type=\"短剧\">\n <i class=\"fas fa-video\"><\/i>\n <span>短剧<\/span>\n <\/div>\n <\/div>\n <div class=\"selected-info\" style=\"margin-bottom: 10px; background: rgba(45, 157, 120, 0.08); border-color: rgba(45, 157, 120, 0.2);\">\n 推荐选择全部,如需要搜索特定的资源可以搜索的时候使用 <span class=\"highlight\">书名@来源<\/span> 进行搜索\n <\/div>\n <div id=\"sourceOptions\" class=\"source-options\"><\/div>\n <div class=\"selected-info\" id=\"selectedInfo\">\n 当前选中: <span id=\"selectedCount\">0<\/span> 个来源\n <\/div>\n <\/div>\n \n <div id=\"hiddenSettings\" style=\"display: none;\">\n <script type=\"application\/json\" id=\"settingsJson\">\n ${JSON.stringify(data || {\"小说\":\"全部\",\"听书\":\"全部\",\"漫画\":\"全部\",\"短剧\":\"全部\",\"搜索模式\":\"小说\",\"显示图片\":\"true\",\"完整简介\":\"true\",\"同步书架\":\"false\",\"同步弹窗\":\"true\",\"网络模式\":\"服务器\",\"强制搜索\":\"false\",\"目录显示来源\":\"true\"})}\n <\/script>\n <div id=\"settingsDiv\">\n <span id=\"novelSource\">${(data && data.小说) || '全部'}<\/span>\n <span id=\"audioSource\">${(data && data.听书) || '全部'}<\/span>\n <span id=\"comicSource\">${(data && data.漫画) || '全部'}<\/span>\n <span id=\"dramaSource\">${(data && data.短剧) || '全部'}<\/span>\n <span id=\"searchModeValue\">${(data && data.搜索模式) || '小说'}<\/span>\n <span id=\"showImageSwitchValue\">${(data && data.显示图片) || 'true'}<\/span>\n <span id=\"fullDescSwitchValue\">${(data && data.完整简介) || 'true'}<\/span>\n <span id=\"syncBookshelfSwitchValue\">${(data && data.同步书架) || 'false'}<\/span>\n <span id=\"syncPopupSwitchValue\">${(data && data.同步弹窗) || 'true'}<\/span>\n <span id=\"networkModeValue\">${(data && data.网络模式) || '服务器'}<\/span>\n <span id=\"forceSearchSwitchValue\">${(data && data.强制搜索) || 'false'}<\/span>\n <span id=\"showSourceInTocValue\">${(data && data.目录显示来源) || 'true'}<\/span>\n <\/div>\n <\/div>\n <\/div>\n<\/div>\n\n<script>\nconst SOURCES = ${JSON.stringify(config)};\nlet settings = ${JSON.stringify(data)};\nlet currentType = '小说';\n\ndocument.getElementById('tabSource').addEventListener('click', () => {\n document.getElementById('tabSource').classList.add('active');\n document.getElementById('tabBookSource').classList.remove('active');\n document.getElementById('contentSource').classList.add('active');\n document.getElementById('contentBookSource').classList.remove('active');\n});\n\ndocument.getElementById('tabBookSource').addEventListener('click', () => {\n document.getElementById('tabBookSource').classList.add('active');\n document.getElementById('tabSource').classList.remove('active');\n document.getElementById('contentBookSource').classList.add('active');\n document.getElementById('contentSource').classList.remove('active');\n});\n\nconst subtabs = document.querySelectorAll('.source-subtab');\nsubtabs.forEach(tab => {\n tab.addEventListener('click', () => {\n subtabs.forEach(t => t.classList.remove('active'));\n tab.classList.add('active');\n currentType = tab.dataset.type;\n renderSourceOptions();\n });\n});\n\nfunction renderSourceOptions() {\n const optionsContainer = document.getElementById('sourceOptions');\n const key = \\`搜索\\${currentType}\\`;\n const sources = SOURCES[key] || [];\n const savedValue = settings[currentType] || '';\n const selectedValues = savedValue === '全部' ? ['全部'] : (savedValue ? savedValue.split(',') : []);\n \n optionsContainer.innerHTML = '';\n \n const allOption = document.createElement('div');\n allOption.className = \\`source-option all \\${selectedValues.includes('全部') ? 'selected' : ''}\\`;\n allOption.textContent = '全部';\n allOption.addEventListener('click', () => selectOption('全部'));\n optionsContainer.appendChild(allOption);\n \n sources.forEach(source => {\n const option = document.createElement('div');\n option.className = \\`source-option \\${selectedValues.includes(source) ? 'selected' : ''}\\`;\n option.textContent = source;\n option.addEventListener('click', () => selectOption(source));\n optionsContainer.appendChild(option);\n });\n \n updateSelectedInfo();\n}\n\nfunction updateHiddenSettings() {\n const jsonEl = document.getElementById('settingsJson');\n if (jsonEl) {\n jsonEl.textContent = JSON.stringify(settings);\n }\n \n const typeMap = {\n '小说': 'novelSource',\n '听书': 'audioSource',\n '漫画': 'comicSource',\n '短剧': 'dramaSource',\n '搜索模式': 'searchModeValue',\n '显示图片': 'showImageSwitchValue',\n '完整简介': 'fullDescSwitchValue',\n '同步书架': 'syncBookshelfSwitchValue',\n '同步弹窗': 'syncPopupSwitchValue',\n '网络模式': 'networkModeValue',\n '强制搜索': 'forceSearchSwitchValue',\n '目录显示来源': 'showSourceInTocValue'\n };\n \n Object.keys(typeMap).forEach(type => {\n const el = document.getElementById(typeMap[type]);\n if (el) {\n el.textContent = settings[type] != undefined ? settings[type] : (type.includes('开关') ? 'false' : '全部');\n }\n });\n}\n\nfunction setupBookSourceSettings() {\n document.getElementById('searchMode').addEventListener('change', (e) => {\n settings['搜索模式'] = e.target.value;\n updateHiddenSettings();\n });\n \n document.getElementById('showImageSwitch').addEventListener('change', (e) => {\n settings['显示图片'] = e.target.checked ? 'true' : 'false';\n updateHiddenSettings();\n });\n \n document.getElementById('fullDescSwitch').addEventListener('change', (e) => {\n settings['完整简介'] = e.target.checked ? 'true' : 'false';\n updateHiddenSettings();\n });\n \n document.getElementById('syncBookshelfSwitch').addEventListener('change', (e) => {\n settings['同步书架'] = e.target.checked ? 'true' : 'false';\n updateHiddenSettings();\n \n const syncPopupSection = document.getElementById('syncPopupSection');\n const syncPopupSwitch = document.getElementById('syncPopupSwitch');\n if (e.target.checked) {\n syncPopupSection.style.display = 'flex';\n if (settings['同步弹窗'] == undefined) {\n settings['同步弹窗'] = 'true';\n syncPopupSwitch.checked = true;\n updateHiddenSettings();\n }\n } else {\n syncPopupSection.style.display = 'none';\n }\n });\n \n document.getElementById('syncPopupSwitch').addEventListener('change', (e) => {\n settings['同步弹窗'] = e.target.checked ? 'true' : 'false';\n updateHiddenSettings();\n });\n \n document.getElementById('networkMode').addEventListener('change', (e) => {\n settings['网络模式'] = e.target.value;\n updateHiddenSettings();\n });\n \n document.getElementById('forceSearchSwitch').addEventListener('change', (e) => {\n settings['强制搜索'] = e.target.checked ? 'true' : 'false';\n updateHiddenSettings();\n });\n \n document.getElementById('showSourceInTocSwitch').addEventListener('change', (e) => {\n settings['目录显示来源'] = e.target.checked ? 'true' : 'false';\n updateHiddenSettings();\n });\n}\n\nfunction initBookSourceSettings() {\n const searchModeEl = document.getElementById('searchMode');\n if (searchModeEl && settings['搜索模式']) {\n searchModeEl.value = settings['搜索模式'];\n }\n \n const showImageEl = document.getElementById('showImageSwitch');\n if (showImageEl) {\n showImageEl.checked = settings['显示图片'] != 'false';\n }\n \n const fullDescEl = document.getElementById('fullDescSwitch');\n if (fullDescEl) {\n fullDescEl.checked = settings['完整简介'] != 'false';\n }\n \n const syncBookshelfEl = document.getElementById('syncBookshelfSwitch');\n if (syncBookshelfEl) {\n syncBookshelfEl.checked = settings['同步书架'] === 'true';\n }\n \n const syncPopupSection = document.getElementById('syncPopupSection');\n const syncPopupSwitch = document.getElementById('syncPopupSwitch');\n if (syncPopupSwitch) {\n syncPopupSwitch.checked = settings['同步弹窗'] != 'false';\n }\n if (syncPopupSection) {\n syncPopupSection.style.display = (settings['同步书架'] === 'true') ? 'flex' : 'none';\n }\n \n const networkModeEl = document.getElementById('networkMode');\n if (networkModeEl && settings['网络模式']) {\n networkModeEl.value = settings['网络模式'];\n }\n \n const forceSearchEl = document.getElementById('forceSearchSwitch');\n if (forceSearchEl) {\n forceSearchEl.checked = settings['强制搜索'] === 'true';\n }\n \n const showSourceInTocEl = document.getElementById('showSourceInTocSwitch');\n if (showSourceInTocEl) {\n showSourceInTocEl.checked = settings['目录显示来源'] != 'false';\n }\n}\n\nfunction selectOption(value) {\n const key = \\`搜索\\${currentType}\\`;\n const sources = SOURCES[key] || [];\n \n if (value === '全部') {\n settings[currentType] = '全部';\n updateHiddenSettings();\n renderSourceOptions();\n return;\n }\n \n let currentValue = settings[currentType] || '';\n \n if (currentValue === '全部') {\n currentValue = '';\n }\n \n let values = currentValue ? currentValue.split(',') : [];\n \n const index = values.indexOf(value);\n if (index > -1) {\n values.splice(index, 1);\n } else {\n values.push(value);\n }\n \n if (values.length === 0) {\n settings[currentType] = '全部';\n } else {\n settings[currentType] = values.join(',');\n }\n \n updateHiddenSettings();\n renderSourceOptions();\n}\n\nfunction updateSelectedInfo() {\n const key = \\`搜索\\${currentType}\\`;\n const sources = SOURCES[key] || [];\n const savedValue = settings[currentType] || '';\n \n if (savedValue === '全部') {\n document.getElementById('selectedCount').textContent = '全部';\n } else {\n const count = savedValue ? savedValue.split(',').length : 0;\n document.getElementById('selectedCount').textContent = count;\n }\n}\n\ndocument.addEventListener('DOMContentLoaded', () => {\n let savedSettings = {};\n try {\n const jsonEl = document.getElementById('settingsJson');\n if (jsonEl) {\n savedSettings = JSON.parse(jsonEl.textContent);\n }\n } catch (e) {\n console.error('读取初始设置失败:', e);\n }\n \n settings = {\n '小说': savedSettings['小说'] || '全部',\n '听书': savedSettings['听书'] || '全部',\n '漫画': savedSettings['漫画'] || '全部',\n '短剧': savedSettings['短剧'] || '全部',\n '搜索模式': savedSettings['搜索模式'] || '小说',\n '显示图片': savedSettings['显示图片'] || 'true',\n '完整简介': savedSettings['完整简介'] || 'true',\n '同步书架': savedSettings['同步书架'] || 'false',\n '同步弹窗': savedSettings['同步弹窗'] || 'true',\n '网络模式': savedSettings['网络模式'] || '服务器',\n '强制搜索': savedSettings['强制搜索'] || 'false',\n '目录显示来源': savedSettings['目录显示来源'] || 'true'\n };\n \n initBookSourceSettings();\n setupBookSourceSettings();\n renderSourceOptions();\n});\n<\/script>\n<\/body>\n<\/html>\n `\n let body;\n try {\n body = java.startBrowserAwait(`data:text\/html;base64,${java.base64Encode(html)}`, '光遇书源设置', false).body();\n } catch (e) {\n let islyc = this.checkEnv();\n if (islyc == \"安卓\") {\n java.longToast(\"\\n当前软件版本过低,请更新软件,或者使用底部备用设置方案~\");\n } else {\n java.longToast(\"\\n请点击页面右上角√才能设置成功哦~\");\n }\n return;\n }\n \n let settings = {};\n try {\n const getSpanText = (spanId) => {\n const regex = new RegExp('id=\"' + spanId + '\"\\s*>\\s*([^<]*?)\\s*<\\\/span>');\n const match = body.match(regex);\n return match ? match[1].trim() : \"\";\n };\n settings = {\n '搜索模式': getSpanText('searchModeValue'),\n '显示图片': getSpanText('showImageSwitchValue'),\n '完整简介': getSpanText('fullDescSwitchValue'),\n '同步书架': getSpanText('syncBookshelfSwitchValue'),\n '同步弹窗': getSpanText('syncPopupSwitchValue'),\n '网络模式': getSpanText('networkModeValue'),\n '强制搜索': getSpanText('forceSearchSwitchValue'),\n '目录显示来源': getSpanText('showSourceInTocValue'),\n '小说': getSpanText('novelSource'),\n '听书': getSpanText('audioSource'),\n '漫画': getSpanText('comicSource'),\n '短剧': getSpanText('dramaSource'),\n };\n const settingsText = Object.keys(settings).map(key => `${key}: ${settings[key]}`).join('\\n');\n java.longToast(\"\\n\" + settingsText);\n\n } catch (e) {\n settings = {\n '搜索模式': '小说',\n '显示图片': 'true',\n '完整简介': 'true',\n '同步书架': 'false',\n '同步弹窗': 'true',\n '网络模式': '服务器',\n '强制搜索': 'false',\n '目录显示来源': 'true',\n '小说': '全部',\n '听书': '全部',\n '漫画': '全部',\n '短剧': '全部',\n };\n }\n this.setVariable(\"更多设置\", settings, false)\n}\n",
"lastUpdateTime": "1779419473953",
"loginUi": "[\n {\n \"name\": \"🔭 切换线路\",\n \"type\": \"button\",\n \"action\": \"getServerSettings()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n },{\n \"name\": \"⚙️ 书源设置\",\n \"type\": \"button\",\n \"action\": \"getHtmlSettings()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"🗨 段评设置\",\n \"type\": \"button\",\n \"action\": \"getSvgSettings()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n },{\n \"name\": \"邮箱\",\n \"type\": \"text\"\n }, {\n \"name\": \"密码\",\n \"type\": \"password\"\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\": \"register()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \" 🔚 退出登录\",\n \"type\": \"button\",\n \"action\": \"logout()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n },\n {\n \"name\": \"🏝用户后台\",\n \"type\": \"button\",\n \"action\": \"user()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n },{\n \"name\": \"📴 清理设备\",\n \"type\": \"button\",\n \"action\": \"clearDevice()\",\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 \"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\": \"fb()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n },{\n \"name\": \"✳️ 更新配置\",\n \"type\": \"button\",\n \"action\": \"_getCloudSettings()\",\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\": \"deleteVariable()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n }, {\n \"name\": \"===下方为备用设置方案===\",\n \"type\": \"button\",\n \"action\": \"\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n }, {\n \"name\": \"📖搜索小说\",\n \"type\": \"button\",\n \"action\": \"ste('小说')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"🎧搜索听书\",\n \"type\": \"button\",\n \"action\": \"ste('听书')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"🎥搜索短剧\",\n \"type\": \"button\",\n \"action\": \"ste('短剧')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"🎨搜索漫画\",\n \"type\": \"button\",\n \"action\": \"ste('漫画')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"段评开关\",\n \"type\": \"button\",\n \"action\": \"ste('段评')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n }, {\n \"name\": \"图片开关\",\n \"type\": \"button\",\n \"action\": \"ste('图片')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n }, {\n \"name\": \"简介开关\",\n \"type\": \"button\",\n \"action\": \"ste('简介')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n }, {\n \"name\": \"同步书架\",\n \"type\": \"button\",\n \"action\": \"ste('同步')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n }, {\n \"name\": \"强制搜索\",\n \"type\": \"button\",\n \"action\": \"ste('强制')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n }, {\n \"name\": \"网络模式\",\n \"type\": \"button\",\n \"action\": \"ste('网络')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.25\n }\n }, {\n \"name\": \"目录显示来源开关\",\n \"type\": \"button\",\n \"action\": \"ste('目录')\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n }, {\n \"name\": \"发现页来源(支持的平台请前往源变量中查看)\",\n \"type\": \"text\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n }, {\n \"name\": \"搜索来源(支持的平台请前往源变量中查看,多个来源用英文逗号分割)\",\n \"type\": \"text\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 1\n }\n }, {\n \"name\": \"设置发现页来源\",\n \"type\": \"button\",\n \"action\": \"setFindSource()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \"设置搜索来源\",\n \"type\": \"button\",\n \"action\": \"setSearchSource()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }\n]",
"loginUrl": "\/\/登陆\nfunction login(flag) {\n const token = getToken();\n if (String(token).length > 10) {\n java.longToast(flag ? '\\n当前✅️已登录,请🚫退出登录后重新登录' : '\\n✅️已登录');\n return true;\n }\n if (flag == undefined) {\n result = JSON.parse(source.getLoginInfo())\n } else {\n java.longToast(\"\\n\\n💞正在登录中...\")\n }\n let email = result.邮箱;\n let pwd = result.密码;\n\n if (!email || !pwd) {\n java.longToast('\\n请先输入账号密码!');\n return false;\n }\n if (!email.includes('@')) {\n java.longToast('\\n请输入正确的邮箱格式!');\n return false;\n }\n try {\n let data = request('\/login_api','POST',{\n register_email: email,\n password: pwd\n })\n const response = JSON.parse(data);\n if (response.code == 0) {\n setAllCookies(`qttoken=${response.key}`);\n java.longToast(\"\\n✅️登录成功\");\n return true;\n } else {\n let msg = response.msg;\n if (msg.includes('多次登录失败')) {\n java.longToast('\\n💔多次登录失败,请检查账号密码,切换线路后重试或等2小时后重试!');\n } else {\n java.longToast('\\n💔' + (msg || \"登录失败,请重试!\"));\n }\n return false;\n }\n } catch (error) {\n java.longToast('\\n💔登录失败,服务器错误,请更换线路后重试!\\n' + error);\n return false;\n }\n}\n\n\/\/ 用户注册\nfunction register() {\n java.startBrowserAwait(BaseUrl() + '\/register', '光遇看书注册');\n}\n\n\/\/用户后台\nfunction user() {\n const token = getToken();\n if (String(token).length < 10) {\n java.longToast('\\n🤔请先登陆');\n return;\n }\n java.startBrowserAwait(BaseUrl() + '\/user', '光遇看书用户后台');\n}\n\n\/\/ 退出登陆\nfunction logout() {\n removeAllCookies();\n}\n\n\/\/ 获取云端配置\nfunction _getCloudSettings() {\n getCloudSettings(true)\n}\n\n\/\/打赏\nfunction vip() {\n const token = getToken();\n if (String(token).length < 10) {\n java.longToast('\\n🤔请先登陆');\n return;\n }\n java.startBrowserAwait(BaseUrl() + '\/coffee', '光遇聚合会员开通');\n}\n\nfunction formatDate(timestamp) {\n const date = new Date(timestamp * 1000);\n return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;\n}\n\nfunction isVips(res) {\n const vipEndTime = res.vip_end_time;\n const isVipValue = res.is_vip;\n\n let vipType;\n if (isVipValue == 0) {\n return '普通用户';\n } else if (isVipValue == 1) {\n vipType = 'VIP';\n } else {\n vipType = 'SVIP';\n }\n\n if (!vipEndTime || vipEndTime == 0) {\n return `${vipType} (已过期)`;\n }\n\n const currentTime = Math.floor(Date.now() \/ 1000);\n const remainingDays = Math.ceil((vipEndTime - currentTime) \/ (24 * 60 * 60));\n\n if (currentTime > vipEndTime) {\n return `${vipType} (已过期)`;\n }\n\n if (remainingDays <= 7) {\n return `${vipType} 剩余${remainingDays}天`;\n }\n\n if (vipEndTime >= 1912946812) {\n return `${vipType} (永久)`;\n }\n\n return `${vipType}(${formatDate(vipEndTime)})`;\n}\n\nfunction checkStatus() {\n const token = getToken();\n if (String(token).length < 10) {\n java.longToast('\\n🤔请先登陆');\n return;\n }\n java.longToast('\\n\\n♻️查询中...');\n\n try {\n const res = JSON.parse(request(`\/user_api`, 'POST'));\n\n if (res.id == undefined) {\n throw new Error(res.msg || '获取用户信息失败');\n }\n\n result.邮箱 = res.email;\n source.putLoginInfo(result);\n\n let devices = 0;\n try {\n devices = res.device ? Object.keys(JSON.parse(res.device)).length : 0;\n } catch (e) {\n devices = res.device ? 1 : 0;\n }\n\n const isVip = isVips(res);\n const today = java.timeFormat(new Date()).slice(0, 10);\n const lastReadDate = res.last_read_time ? java.timeFormat(res.last_read_time * 1000).slice(0, 10) : '';\n const todayReadCount = today == lastReadDate ? res.day_read_count : 0;\n\n const tips = `\n${BaseUrl()}\n┏┅┅┅┅┅┅┱┄┄┄┄┄┄┄┄┄┄┐\n 🧢昵称 ${(res.nickname || '未设置').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🗒️今日阅读 ${todayReadCount.toString().padEnd(20, '\\t')}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n📚累计阅读 ${res.all_read_count.toString().padEnd(20, '\\t')}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n🕓最后阅读 ${(res.last_read_time ? 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\n java.log(tips);\n java.longToast(tips);\n\n } catch (e) {\n java.toast(`\\n检测登录失败\\n${e.message}`);\n }\n}\n\nfunction clearDevice() {\n const token = getToken();\n if (String(token).length < 10) {\n java.longToast('\\n🤔请先登陆');\n return;\n }\n let res = request(`\/clear`, \"POST\");\n res = JSON.parse(res);\n java.toast(res.code == 0 ? \"\\n\\n📴设备清除成功\" : res.msg)\n \/\/Packages.java.lang.Thread.sleep(500)\n \/\/checkStatus()\n}\n\nfunction fb() {\n java.startBrowser('http:\/\/vip.gyks.cf', '光遇看书');\n}\n\nfunction setFindSource() {\n\tlet findSource = result[\"发现页来源(支持的平台请前往源变量中查看)\"];\n\tif (String(findSource)==\"\" || String(findSource) == \"undefined\") {\n\t\tfindSource = \"番茄\";\n\t\t}\n\tsetVariable(\"发现页来源\",findSource);\n java.longToast(`\\n已设置发现页来源为:${findSource}\\n设置后请刷新发现页!`)\n \n\t}\n\t\n\t\nfunction setSearchSource() {\n\tlet searchSource = result[\"搜索来源(支持的平台请前往源变量中查看,多个来源用英文逗号分割)\"];\n\tif (String(searchSource)==\"\" || String(searchSource) == \"undefined\") {\n\t\tsearchSource = '全部';\n\t\t}\n\tlet moreSettings = getVariable(\"更多设置\") || {};\n\tlet tab = moreSettings && moreSettings['搜索模式'] || '小说';\n\tmoreSettings[tab] = searchSource;\n\tsetVariable(\"更多设置\",moreSettings)\n java.longToast(`\\n已设置搜索来源为:${tab}-${moreSettings[tab]}\\n设置后请重新搜索`)\n\t}\n\n",
"respondTime": 180000,
"ruleBookInfo": {
"author": "$.author",
"canReName": "1",
"coverUrl": "$.thumb_url",
"init": "<js>\nlet moreSettings = getVariable('更多设置');\n\n\nfunction fetchBookDetail(result) {\n\t let res = {};\n\t \/\/java.log(baseUrl)\n\t \/\/java.longToast(result)\n\t if (String(baseUrl).startsWith(\"data:\")) {\n res = JSON.parse(java.hexDecodeToString(result));\n } else {\n \treturn result;\n \t}\n const book_id = res.book_id;\n const tab = res.tab;\n const sources = res.sources || res.source;\n const url = res.url;\n let html = \"\";\n const proxy = moreSettings && moreSettings['网络模式'] || '服务器';\n\n if (url && proxy == \"本地\") {\n html = fetchHtmlWithFallback(url, sources);\n }\n\n const varia = getAndFormatCustomVariable();\n const requestUrl = `\/detail?book_id=${book_id}&source=${sources}&tab=${tab}&variable=${varia}`;\n\n java.log(requestUrl);\n return request(requestUrl, 'POST', { html: html });\n}\n\nfunction fetchHtmlWithFallback(url, sources) {\n let html;\n let ck69 = '';\n let headers = {};\n let op = '';\n\n if (sources == '69书吧') {\n ck69 = String(cookie.getCookie('https:\/\/www.69shuba.com'));\n headers = {\n \"Referer\": url,\n \"Cookie\": ck69,\n \"User-Agent\": java.getWebViewUA()\n };\n op = JSON.stringify({ \"headers\": headers });\n html = java.ajax(`${url},${op}`);\n } else {\n html = java.ajax(url);\n }\n\n if (html.includes(\"Just a moment...\") && sources == '69书吧') {\n cookie.removeCookie('https:\/\/www.69shuba.com');\n java.longToast('需要真人验证,请进入任意书籍详情页过验证');\n html = java.startBrowserAwait(url, \"需要真人验证,请进入任意书籍详情页过验证\").body();\n }\n\n return html;\n}\n\nfunction getAndFormatCustomVariable() {\n let varia = String(book.getVariable('custom'));\n if (varia === 'null') {\n varia = '';\n }\n return JSON.stringify({ 'custom': varia });\n}\nfetchBookDetail(result)\n<\/js>$.data",
"intro": "<js>\nconst {\n source:sources,\n book_id,\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 read_count,\n read_count_all\n} = result;\nlet moreSettings = getVariable('更多设置');\n\n\nconst proxy = (moreSettings && moreSettings['网络模式'] || '服务器') == \"本地\" ? \"本地网络\" : \"服务器网络\";\n\nif ((book.readConfig && book.readConfig.useReplaceRule) == null) {\n book.setUseReplaceRule(false);\n}\n\nlet nickname = '账号状态:⚠️ 未登录 | 点击右上角 🔖 登录';\nlet qttoken = getToken();\nif (qttoken.length > 10) {\n nickname = '账号状态:✅已登录';\n}\ntry {\n const user_info = JSON.parse(request(`\/get_avatar`, 'GET'));\n if (user_info.code === 0) {\n nickname = user_info.nickname \n ? `欢迎回来:${user_info.nickname}`\n : `欢迎回来:${user_info.email}(请前往用户后台设置用户名)`;\n }\n} catch (e) {\n \n}\n\nconst loginStatus = nickname;\nconst lightDivider = \"❇️───────❇️───────❇️\";\nconst heavyDivider = \"‎\\n‎\";\n\nconst isValid = (value) => String(value).length > 1;\nlet ctitle = book.durChapterTitle || '未开始';\n\nconst formatAbstract = (text) => {\n return text.split(\"\\n\").map(line => ` ${line}`).join(\"\\n\");\n};\n\nconst buildBasicInfo = (items) => {\n let info = \"\";\n for (let [value, prefix, icon] of items) {\n if (isValid(value)) {\n info += ` ${icon} ${prefix} ${value}\\n`;\n }\n }\n return info;\n};\n\nlet info = `\n 📡 当前服务:${BaseUrl()}\n 🔑 ${loginStatus}\n 🏷 数据来源:${sources}\n 🔄 当前模式:${tab}\n ⚙️ 访问模式:${proxy}\n 📖 阅读至:${ctitle}\n`;\n\nif (tab == \"听书\") {\n let toneId = String(book.getVariable('custom')) || '4';\n if (isValid(book_tts)) {\n info += `${lightDivider}\n 🎵 音色配置:${toneId}\n ✨ AI音色请点击右上角书籍变量填写相关值,真人听书请重新搜索选择带有主播的书籍\n ${book_tts}\n`;\n }\n}\n\nconst fullBasicInfo = buildBasicInfo([\n [tags, \"书籍分类:\", \"🌈\"],\n [role, \"书籍主角:\", \"👑\"],\n [last_chapter_title, \"最新章节:\", \"📚\"],\n [last_chapter_update_time, \"更新时间:\", \"⏳\"],\n [word_number, \"书籍字数:\", \"📝\"],\n [status, \"书籍状态:\", \"🚩\"],\n [score, \"书籍评分:\", \"⭐\"],\n [read_count, \"今阅读人数:\", \"📃\"],\n [read_count_all, \"总阅读人数:\", \"📈\"]\n]);\n\nif (fullBasicInfo) {\n info += `${lightDivider}\\n${fullBasicInfo}`;\n}\n\nif (isValid(abstract)) {\n info += `${heavyDivider}\n 📖 书籍简介:\n${formatAbstract(abstract)}\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\nconst abstractMode = moreSettings && moreSettings['完整简介'] || 'true';\nif (abstractMode == \"false\") {\n const simplifiedBasicInfo = buildBasicInfo([\n [last_chapter_title, \"最新章节:\", \"📚\"],\n [last_chapter_update_time, \"更新时间:\", \"⏳\"],\n [word_number, \"书籍字数:\", \"📊\"],\n [status, \"书籍状态:\", \"🚩\"],\n [score, \"书籍评分:\", \"⭐\"]\n ]);\n \n info = `‎\\n🏷 数据来源:${sources}\\n${simplifiedBasicInfo}${isValid(abstract) ? `\\n${heavyDivider}\\n 📖 书籍简介:\\n${formatAbstract(abstract)}` : heavyDivider}`;\n} else {\n info = info.split(\"\\n\").map(line => line.replace(\/^ {4}\/, \"\")).join(\"\\n\");\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 book_name = result.book_name;\nlet author = result.author;\nlet abstract = result.abstract;\nlet thumb_url = result.thumb_url;\nlet tab = result.tab || getVariable('类型');\nlet url = result.toc_url || \"\";\nlet gycatalog = {\n\t book_name,\n\t author,\n\t abstract,\n\t thumb_url,\n book_id,\n sources,\n tab,\n url,\n};\ngycatalog = java.base64Encode(JSON.stringify(gycatalog));\n`data:;base64,${gycatalog},{\"type\":\"gycatalog\"}`;\n<\/js>",
"wordCount": "$.word_number"
},
"ruleContent": {
"content": "<js>\nlet DEFAULT_TONE_ID = '4';\nlet CONTENT_URL = '\/content';\nlet CONTENT_URL_WITH_REVIEW = '\/content?review=1';\nlet VERSION = localVersion;\nlet SOURCES_WITH_REVIEW = ['番茄', '七猫', '塔读', 'QQ阅读', 'svip_QQ阅读'];\nlet SOURCES_VIDEO = ['毒舌影视', 'NT动漫'];\nlet moreSettings = getVariable('更多设置');\nconst proxy = moreSettings && moreSettings['网络模式'] || '服务器';\nconst reading = moreSettings && moreSettings['同步书架'] || 'false';\nconst reading_toast = moreSettings && moreSettings['同步弹窗'] || 'true';\n\nresult = String(java.hexDecodeToString(result));\nlet res = JSON.parse(result);\nlet { book_id, item_id, tab, title, sources, url } = res;\n\nlet islyc = checkEnv();\ntry {\n book.imageStyle = islyc == '改版' ? 'FULL' : 'TEXT';\n} catch (e) {\n \n}\n\nconst syncBookShelf = (book) => {\n if (!book) {\n return;\n }\n let book_order = book.order;\n if (book_order!=0) {\n \t book_order = 1;\n \t} else {\n \t\tbook_order = 2;\n \t\t}\n \n if (reading != \"true\") {\n return;\n }\n\n try {\n const ritem = item_id;\n \nlet book_info = {\n book_name:book.name,\n author:book.author,\n \/\/abstract:book.abstract,\n thumb_url:book.coverUrl,\n book_id,\n tab,\n source: sources\n };\n if (!book_info || typeof book_info != \"object\") {\n return;\n }\n const rurl = `\/add_book_to_book_shelf`;\n book_info.read_status = book_order;\n book_info.last_chapter_item_id = item_id;\n book_info.last_chapter_title = chapter.title || \"\";\n \/\/java.toast(JSON.stringify(book_info));\n \ttry {\n \t\tconst check_book_url = `\/check_book_in_book_shelf`;\n checkResponse = request(check_book_url, \"POST\", {book_id,source:sources,tab});\n} catch (e) {\n java.log(`检查书籍请求失败: ${e}`);\n}\n \n const check_data = parseJsonSafely(checkResponse);\n try {\n if (check_data.data.id) {\n book_info.id = check_data.data.id;\n const uurl = `\/update_book_shelf`;\n let up = request(uurl, \"POST\", book_info);\n if (reading_toast == \"true\") {\n \t java.toast(up)\n \t}\n \n } else {\n let up = request(rurl, \"POST\", book_info);\n if (reading_toast == \"true\") {\n \t java.toast(up)\n \t}\n }\n } catch (e) {\n try {\n let up = request(rurl, \"POST\", book_info);\n java.toast(up)\n } catch (e) {\n if (reading_toast == \"true\") {\n \t java.toast(`书架操作失败: ${e}`);\n \t}\n }\n\n }\n } catch (error) {\n java.log(`书籍同步流程异常: ${error}`);\n \n if (reading_toast == \"true\") {\n \t java.longToast(\"\\n同步阅读进度失败,但不影响阅读,可以前往登录关闭书架同步功能。\");\n \t}\n }\n};\n\n\/\/java.log(book)\n\n\nif (book.durChapterIndex == chapter.index) {\n\ttry {syncBookShelf(book)} catch {}\n\t}\n\n\n\n\nlet varia1 = String(book.getVariable('custom')) || '';\nlet varia = JSON.stringify({ custom: varia1 });\n\n\nlet html = '';\n\nif (url && proxy === '本地') {\n if (sources === '69书吧') {\n let ck69 = String(cookie.getCookie('https:\/\/www.69shuba.com'));\n let headers = {\n Referer: url,\n Cookie: ck69,\n 'User-Agent': java.getWebViewUA()\n };\n let op = JSON.stringify({ headers });\n html = java.ajax(`${url},${op}`);\n } else {\n html = java.ajax(url);\n }\n \n if (html.includes('Just a moment...') && sources == '69书吧' && book.durChapterIndex == chapter.index) {\n cookie.removeCookie('https:\/\/www.69shuba.com');\n java.longToast('需要真人验证,请进入任意书籍详情页过验证');\n html = java.startBrowserAwait(url, '需要真人验证,请进入任意书籍详情页过验证').body();\n }\n}\n\nlet tone_id = varia1 || DEFAULT_TONE_ID;\nlet base_url = BaseUrl();\nlet dpSettings = getVariable('段评设置');\nlet para = dpSettings && dpSettings['段评开关'] || 'true';\nlet show_img = moreSettings && moreSettings['显示图片'] || 'true';\n\nlet params = {\n html,\n item_id,\n source: sources,\n tab,\n tone_id,\n variable: varia,\n version: VERSION\n};\n\nlet hasReview = SOURCES_WITH_REVIEW.includes(sources) && para==\"true\" && tab == '小说';\nlet content_url = hasReview ? CONTENT_URL_WITH_REVIEW : CONTENT_URL;\n\nlet data = request(content_url, 'POST', params);\ntry {\n data = JSON.parse(data);\n if (data.msg) {\n java.toast(data.msg);\n }\n} catch (e) {\n \/\/console.error('Parse content data failed:', e);\n}\n\nlet content = data.content || '';\n\nif (show_img == 'false') {\n content = removeAllImgTags(content);\n}\n\nif (hasReview) {\n let fqssionid = java.getCookie('fanqienovel.com', 'sessionid');\n content = content\n .replace(\/ident=\"\/g, `ident=\"${base_url}`)\n .replace(\/book_id=\/g, `book_id=${book_id}&ssionid=${fqssionid}`);\n let deviceType = '安卓';\n try {\n \tjava.deviceID();\n \tdeviceType = '苹果'\n \t} catch {}\n content = deviceType == '苹果' \n ? paraForiOS(content, sources) \n : paraForAndroid(content, sources);\n}\n\ndata = JSON.stringify({ content });\n\nlet isVideoSource = SOURCES_VIDEO.some(src => sources.includes(src));\nlet isVideoTab = tab == '短剧' || tab == '视频';\nif (islyc != '苹果' && (isVideoTab || isVideoSource)) {\n data = JSON.stringify({\n content: `【右上角刷新】开启播放(下一集请切换下一章刷新)\\n播放直链:\\n${content}`\n });\n \n if (book.durChapterIndex == chapter.index) {\n let video_url = `${base_url}\/online_video?book_id=${book_id}&source=${sources}&tab=${tab}`;\n if (isVideoSource) {\n video_url = content;\n }\n \/\/java.openVideoPlayer(content, title)\n java.startBrowser(video_url, title);\n java.toast('正在加载视频...');\n }\n}\n\ndata;\n<\/js>$.content",
"title": "@js:\nresult = String(java.hexDecodeToString(result))\nJSON.parse(result)['title'].replace(JSON.parse(result)['sources']+':','')"
},
"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 gydetail = {\n book_id: book_id,\n sources: sources,\n tab: tab,\n url: url\n}\ngydetail = java.base64Encode(JSON.stringify(gydetail));\n`data:;base64,${gydetail},{\"type\":\"gydetail\"}`\n<\/js>",
"coverUrl": "$.thumb_url",
"intro": "$.abstract",
"kind": "{{$.status}},{{$.score}},{{$.tags}},{{$.last_chapter_update_time}}",
"lastChapter": "{{$.source}} {{$.last_chapter_title}}",
"name": "$.book_name##(别名:.*?)",
"wordCount": "$.word_number"
},
"ruleSearch": {
"author": "$.author",
"bookList": "<js>\nconst res = JSON.parse(java.hexDecodeToString(result));\nconst {\n key,\n tab,\n sourcesKey,\n page,\n disabled_sources\n} = res;\nlet url = `\/search?title=${key}&tab=${tab}&source=${sourcesKey}&page=${page}&disabled_sources=${disabled_sources}`;\nrequest(url);\n<\/js>\n$.data",
"bookUrl": "<js>\nlet book_id = result.book_id;\nlet sources = result.source;\nlet tab = result.tab || '小说';\nlet url = result.toc_url || '';\n\nlet gydetail = {\n book_id: book_id,\n sources: sources,\n tab: tab,\n url: url\n}\ngydetail = java.base64Encode(JSON.stringify(gydetail));\n`data:;base64,${gydetail},{\"type\":\"gydetail\"}`\n<\/js>",
"checkKeyWord": "我的@番茄",
"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>\nconst res = JSON.parse(java.hexDecodeToString(result));\nconst {\n\tbook_name,\n\tauthor,\n\tabstract,\n\tthumb_url,\n book_id,\n tab,\n sources,\n url\n} = res;\nlet durChapterIndex = book.durChapterIndex;\njava.put('book_id', book_id);\nlet html = \"\";\nlet moreSettings = getVariable('更多设置');\nconst proxy = moreSettings && moreSettings['网络模式'] || '服务器';\nconst showSource = moreSettings && moreSettings['目录显示来源'] || 'true';\nconst gytoken = getToken();\nconst reading = moreSettings && moreSettings['同步书架'] || 'false';\nconst reading_toast = moreSettings && moreSettings['同步弹窗'] || 'true';\n\nif (url && proxy == \"本地\") {\n if (sources == \"69书吧\") {\n const ck69 = String(cookie.getCookie(\"https:\/\/www.69shuba.com\"));\n const headers = {\n \"Referer\": url,\n \"Cookie\": ck69,\n \"User-Agent\": java.getWebViewUA()\n };\n const op = JSON.stringify({\n headers\n });\n html = java.ajax(`${url},${op}`);\n } else {\n html = java.ajax(url);\n }\n\n if (html.includes(\"Just a moment...\") && sources === \"69书吧\") {\n cookie.removeCookie(\"https:\/\/www.69shuba.com\");\n java.longToast(\"需要真人验证,请进入任意书籍详情页过验证\");\n html = java.startBrowserAwait(url, \"需要真人验证,请进入任意书籍详情页过验证\").body();\n java.log(html);\n }\n}\n\n\nconst customVar = String(book.getVariable(\"custom\"));\nconst varia = JSON.stringify({\n custom: customVar == \"null\" ? \"\" : customVar\n});\n\nconst queryString = `book_id=${encodeURIComponent(book_id)}&source=${encodeURIComponent(sources)}&tab=${encodeURIComponent(tab)}&variable=${encodeURIComponent(varia)}`;\nconst data = request(`\/catalog?${queryString}`, \"POST\", {\n html\n});\n\nconst device_type = checkEnv();\nconst typeMap = {\n \"小说\": {\n \"苹果\": 0,\n default: 8\n },\n \"听书\": {\n \"苹果\": 1,\n default: 32\n },\n \"漫画\": {\n \"苹果\": 2,\n default: 64\n },\n \"短剧\": {\n \"苹果\": 3,\n \"备用\": 4,\n default: 8\n }\n};\nconst currentType = typeMap[tab] || typeMap[\"小说\"];\nbook.type = currentType[device_type] || currentType.default;\n\n\n\nconst syncBookShelf = (data, book, gytoken) => {\n if (!book) {\n return;\n }\n let book_order = book.order;\n if (book_order!=0) {\n \t book_order = 1;\n \t} else {\n \t\tbook_order = 2;\n \t\t}\n if (reading != \"true\") {\n return;\n }\n\n try {\n if (!data || !gytoken) {\n java.log(\"缺少必要参数\");\n return;\n }\n\n const parsedData = parseJsonSafely(data);\n if (!parsedData || !parsedData.data || !Array.isArray(parsedData.data) || !parsedData.data[durChapterIndex]) {\n return;\n }\n\n const ritem = parsedData.data[durChapterIndex];\n \nlet book_info = {\n book_name,\n author,\n abstract,\n thumb_url,\n book_id,\n tab,\n source: sources\n };\n if (!book_info || typeof book_info != \"object\") {\n return;\n }\n const rurl = `\/add_book_to_book_shelf`;\n book_info.read_status = book_order;\n book_info.last_chapter_item_id = ritem.item_id || \"\";\n book_info.last_chapter_title = ritem.title || \"\";\n \/\/java.toast(JSON.stringify(book_info));\n \ttry {\n \t\tconst check_book_url = `\/check_book_in_book_shelf`;\n checkResponse = request(check_book_url, \"POST\", {book_id,source:sources,tab});\n} catch (e) {\n java.log(`检查书籍请求失败: ${e}`);\n}\n \n const check_data = parseJsonSafely(checkResponse);\n try {\n let chapterIndex = findItemIndex(parsedData.data,check_data.data.last_chapter_item_id)\n durChapterIndex = chapterIndex;\n if (book.durChapterIndex < chapterIndex) {\n \tbook_info.last_chapter_item_id = check_data.data.last_chapter_item_id;\n \t}\n \n if (chapterIndex != 0 && book.durChapterIndex < chapterIndex) {\n book.durChapterIndex = chapterIndex;\n }\n if (check_data.data.id) {\n book_info.id = check_data.data.id;\n const uurl = `\/update_book_shelf`;\n if (book_order==1){\n \tlet up = request(uurl, \"POST\", book_info);\n if (reading_toast == \"true\") {\n \t java.toast(up)\n \t }\n \t}\n \n } else {\n if (book_order==1){\n let up = request(rurl, \"POST\", book_info);\n if (reading_toast == \"true\") {\n \t java.toast(up)\n \t}\n }}\n } catch (e) {\n try {\n if (book_order==1){\n let up = request(rurl, \"POST\", book_info);\n if (reading_toast == \"true\") {\n \t java.toast(up)\n \t}}\n } catch (e) {\n if (reading_toast == \"true\") {\n \t java.toast(`书架操作失败: ${e}`);\n \t}\n }\n\n }\n } catch (error) {\n java.log(`书籍同步流程异常: ${error}`);\n if (reading_toast == \"true\") {\n \t java.longToast(\"\\n同步阅读进度失败,但不影响阅读,可以前往登录关闭书架同步功能。\");\n \t}\n \n }\n};\n\ntry {syncBookShelf(data, book, gytoken);}catch{}\n\n\nfunction processCatalogList(data) {\n let catalog_list = parseJsonSafely(data).data;\n\n\n if (catalog_list && catalog_list.length > 0) {\n\n let lastItem = catalog_list[catalog_list.length - 1];\n\n\n if (lastItem && typeof lastItem == 'object' && lastItem.source && lastItem.title) {\n\n lastItem.title = lastItem.source + ':' + lastItem.title;\n }\n }\n return JSON.stringify({\n 'data': catalog_list\n });\n}\n\nif (showSource == \"true\") {\n processCatalogList(data);\n} else {\n data;\n}\n\n<\/js>$.data",
"chapterName": "$.title",
"chapterUrl": "<js>\nlet { tab, source: sources, title, item_id } = result;\nlet book_id = java.get('book_id');\nlet base_url = BaseUrl();\nlet url = result.toc_url || \"\";\n\nlet gycontent = { book_id, item_id, title, sources, tab, url };\nlet encodedGycontent = java.base64Encode(JSON.stringify(gycontent));\n\nlet content_url;\n\nif (sources == '卷') {\n content_url = item_id;\n} else if (['番茄', '七猫', '塔读'].includes(sources) && tab == \"小说\") {\n const fqssionid = java.getCookie('fanqienovel.com', 'sessionid');\n const sourcess = sources.replace('svip_', '');\n content_url = `data:;base64,${encodedGycontent},${JSON.stringify({\n type: \"gycontent\",\n js: `book ? result : '${base_url}\/get_review?book_id=${book_id}&item_id=${item_id}&ssionid=${fqssionid}&source=${sourcess}'`\n })}`;\n} else {\n content_url = `data:;base64,${encodedGycontent},${JSON.stringify({ type: \"gycontent\" })}`;\n}\n<\/js>",
"updateTime": "$.first_pass_time"
},
"searchUrl": "<js>\nlet moreSettings = getVariable('更多设置');\n\nlet tab = moreSettings && moreSettings['搜索模式'] || '小说';\n\nlet sourcesKey = moreSettings && moreSettings[tab] || '全部';\n\nlet disabled_sources = moreSettings && moreSettings['强制搜索'] || \"0\";\nif (disabled_sources == \"false\") {\n disabled_sources = \"0\";\n} else if (disabled_sources == \"true\") {\n disabled_sources = \"1\";\n}\nlet gysearch = {\n key: key,\n tab: tab,\n sourcesKey: sourcesKey,\n page: page,\n disabled_sources:disabled_sources\n}\ngysearch = java.base64Encode(JSON.stringify(gysearch));\n`data:;base64,${gysearch},{\"type\":\"gysearch\"}`;\n<\/js>",
"weight": 0
}