[ { "bookSourceComment": "如果是用的官方版阅读,或者低版本阅读,推荐更新阅读以使用新功能,否则很多功能无法使用哦\n软件下载地址:https://legado.qingtian618.com\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": "📖番茄小说", "bookSourceName": "🔅光遇聚合", "bookSourceType": 0, "bookSourceUrl": "光遇聚合", "bookUrlPattern": "https?:\\/\\/(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z0-9-]+(?::\\d+)?\\/detail.*", "customButton": false, "customOrder": 0, "enabled": true, "enabledCookieJar": true, "enabledExplore": true, "eventListener": false, "exploreUrl": "\n\n\nlet moreSettings = getVariable('更多设置');\nvar base_url = BaseUrl();\nvar source_type = getVariable('频道') || '男频';\nlet tab = moreSettings && moreSettings['搜索模式'] || '小说';\nlet sources = getVariable(\"发现页来源\") || moreSettings && moreSettings[tab] || '全部';\nlet islyc = checkEnv();\nlet wordNum = getVariable('字数') || '不限';\nlet update = getVariable('更新') || '不限';\nlet sort = getVariable('排序') || '综合';\nlet wordNumMap = {\n '不限': 'word_num_default',\n '10万字以内': 'word_num_lte10',\n '30万字以内': 'word_num_lte30',\n '50万字以内': 'word_num_lte50',\n '30万字以上': 'word_num_gte30',\n '50万字以上': 'word_num_gte50',\n '100万字以上': 'word_num_gte100',\n '200万字以上': 'word_num_gte200',\n '300万字以上': 'word_num_gte300',\n '500万字以上': 'word_num_gte500'\n};\nlet wordNumParam = wordNumMap[wordNum] || 'word_num_default';\nlet updateMap = {\n '不限': 'creation_status_default',\n '完结': 'creation_status_end',\n '半年内完结': 'creation_status_half_year_end',\n '连载中': 'creation_status_loading',\n '3日内更新': 'creation_status_3day_update',\n '7日内更新': 'creation_status_7day_update',\n '1月内更新': 'creation_status_1month_update'\n};\nlet updateParam = updateMap[update] || 'creation_status_default';\nlet sortMap = {\n '综合': 'sort_default',\n '新书': 'sort_new_book',\n '高分': 'sort_score',\n '字数': 'sort_word_number'\n};\nlet sortParam = sortMap[sort] || 'sort_default';\nlet selected_items = `${wordNumParam},${updateParam},${sortParam}`;\n\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 (sources == \"番茄\" && islyc != \"苹果\" && islyc != \"安卓\") {\n for (var i = 0; i < style_list.length; i++) {\n if (style_list[i].title && style_list[i].title.indexOf(\"热门标签\") != -1) {\n style_list.splice(i, 0,\n createFilter(\n \"字数\",\n [\"不限\",\"10万字以内\",\"30万字以内\",\"50万字以内\",\"30万字以上\",\"50万字以上\",\"100万字以上\",\"200万字以上\",\"300万字以上\",\"500万字以上\"],\n \"不限\",\n \"字数\",\n 0.4,\n \"'字数'\"\n ),\n createFilter(\n \"更新\",\n [\"不限\",\"完结\",\"半年内完结\",\"连载中\",\"3日内更新\",\"7日内更新\",\"1月内更新\"],\n \"不限\",\n \"更新\",\n 0.4,\n \"'更新'\"\n ),\n createFilter(\n \"排序\",\n [\"综合\",\"新书\",\"高分\",\"字数\"],\n \"综合\",\n \"排序\",\n 0.1,\n \"'排序'\"\n )\n );\n break;\n }\n }\n }\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.4,\n \"'类型'\"\n ));\n qtsj.push(createFilter(\n \"频道\",\n [\"男频\", \"女频\"],\n source_type,\n \"频道\",\n 0.4,\n \"'频道'\"\n ));\n qtsj.push(createFilter(\n \"平台\",\n source_list,\n sources,\n \"发现页来源\",\n 1,\n \"'平台'\"\n ));\n let searchKey = getVariable('搜索关键词') || \"\";\n qtsj.push(createText(\n '搜索关键词', \n \"setVariable('搜索关键词',(infoMap['搜索关键词'] || (infoMap.get && infoMap.get('搜索关键词'))))\", \n searchKey , \n 0.5,\n \"'搜索书名或作者'\"\n ));\n \n qtsj.push(createButton(\n '🔍搜索', \n 'exploreSearch()', \n 0.3\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 style_list.forEach(item => {\n if (item.url && typeof item.url == 'string') {\n item.url += '&ssionid='+fqssionid+'&selected_items='+selected_items;\n }\n});\n } catch {}\n} else {\n\tif (islyc == '安卓'){\n\tqtsj.push({\n \"title\": \"下载阅读Sigma最新测试版\",\n \"url\": `{\\{java.longToast(\"本书源推荐使用最新版洛娅橙改版阅读Σ\\\\n请安装后重新导入书源\");java.startBrowser(\"https://legado.qingtian618.com\",\"下载阅读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\n\n", "jsLib": "// 当前书源版本号,切勿修改,否则影响更新的识别\nlet localVersion = '26.10.9';\n\n// 初始服务器列表\nlet hosts = [\n 'https://v1.qingtian618.com',\n 'https://cdn.qingtian618.com',\n 'https://v2.qingtian618.com',\n 'https://v3.qingtian618.com',\n 'https://v4.qingtian618.com',\n 'https://v5.qingtian618.com',\n 'https://v6.qingtian618.com',\n 'https://v7.qingtian618.com'\n];\n\nfunction isSpecialMedia (sources) {\n\tlet SOURCES_SPECIAL_MEDIA = ['毒舌影视', 'NT动漫'];\n\t return SOURCES_SPECIAL_MEDIA.some(src => sources.includes(src));\n\t}\n\nfunction buildDiscussionButton(url,sources) {\n let clickAction =\n `showCmt(\"${url}\",\"${sources}\",'本书讨论')`;\n\n return `\n \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 && k!='搜索关键词') {\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(\"切换提醒\") || \"false\";\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('超时时间') || '10';\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>|]*\\/>/gi;\n return htmlString.replace(imgTagRegex, '');\n}\n\n// 段评气泡生成\nfunction paraForAndroid(content, sources) {\n let {\n java,\n cache,\n source\n } = this;\n const createSvg = this.createSvg.bind(this);\n const createGod = this.createGod.bind(this);\n content = content.replace(\n /

<\\/p>/g,\n (match, ident, src) => {\n return `

${createGod(ident, src)}

`;\n }\n );\n \n\n return content.replace(/

(.*?)(?:)?<\\/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 `

${text}

`;\n } else {\n return `

${text}

`;\n }\n }\n );\n}\n// 创建神评论\nfunction createGod(url,svg) {\n var jc = 'js';\n let islyc = this.checkEnv()\n if (islyc == \"改版\") {\n jc = 'click'\n }\n let cz= `{\"${jc}\":\"showCmt('${url}','段评' )\",\"style\":\"FULL\"}`\n\tlet img = ``\n\tif (islyc == \"苹果\") {\n\t\timg = `
`\n \t}\n\treturn img\n\t}\n\nlet svgs = [\n \" $displayText\",\n\n \"$displayText\",\n\n \"$displayText\",\n\n\n\n \"$displayText\",\n\n\n\n \"$displayText\",\n\n\n\n\n `\n \n $displayText\n`\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,ident) {\n let {\n java,\n cache\n } = this;\n if (ident == null) ident = '段评';\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 + ident);\n } else if (islyc == \"改版\") {\n let paradata = new Date().toLocaleDateString();\n let htmlContent = cache.get(paradata+ident+sources+\"new3\");\n if (!htmlContent || String(htmlContent).indexOf(\"评论\") == -1) {\n java.longToast(`\\n${paradata} 初始化${sources}${ident}页面,请稍等~`);\n try {\n htmlContent = java.ajax(url);\n } catch {\n\n }\n\n cache.put(paradata+ident+sources+\"new3\", htmlContent);\n java.longToast(`\\n${paradata} 初始化${sources}${ident}页面成功~`);\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 + ident);\n }\n }\n}\n\n//源阅段评\nfunction paraForiOS(html, sources) {\n\t let result = html.replace(\n /

<\\/p>/g,\n (match, ident, src) => {\n return `

${createGod(ident, src)}
`;\n }\n );\n result = result.replace(\n /

(.*?)(?:)?<\\/p>/g,\n function(match, text, url, count) {\n if (url && count) {\n const encodedUrl = url.replace(/&/g, '&');\n let displayCount = count;\n const numCount = Number(count);\n if (!isNaN(numCount) && numCount > 99) {\n displayCount = '99';\n }\n return `

${text}
`;\n } else {\n return `
${text}
`;\n }\n }\n );\n return result.replace(/"/g, '\"');\n}\n\n\nfunction endclick(url,sources) {\n\tif (sources == null) sources = '番茄';\n var jc = 'js';\n let islyc = this.checkEnv()\n if (islyc == \"改版\") {\n jc = 'click'\n }\n return `\"${jc}\":\"showCmt('${url}','${sources}','本章说' )\"`\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\nfunction createText(title, action, defaultValue, size, viewName) {\n return {\n title: title,\n type: 'text',\n default: defaultValue || '',\n action: action,\n viewName: viewName || title,\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: size\n }\n };\n }\nfunction createButton(title, action, size) {\n return {\n title: title,\n type: 'button',\n action: action,\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: size\n }\n };\n }\n\nfunction exploreSearch() {\n let {java,source} = this;\n var key = this.getVariable('搜索关键词');\n var tab = this.getVariable('发现页类型') || '小说';\n var sources = this.getVariable('发现页来源') || '全部';\n if (!key) {\n try { java.toast('请输入搜索关键词'); } catch (e) {}\n return;\n }\n if (['t:', 'x:', 'd:', 'm:', 't:', 'x:', 'd:', 'm:'].some(p => key.startsWith(p))) {\n tab = '';\n}\nif(key.includes('@')) {\n\tsources=\"\";\n\t} else {\n\t\tsources = `@${sources}`\n\t\t}\n var tabMap = {\n \t'小说':'x:',\n \t'漫画':'m:',\n \t'听书':'t:',\n \t'短剧':'d:',\n \t'':''\n \t}\n key = `${tabMap[tab]}${key}${sources}`;\n try {\n java.searchBook(key, source.bookSourceName+\"::\"+source.getKey());\n } catch (e1) {\n try { java.open('search', key); } catch (e2) { try { java.toast('搜索失败'); } catch (e3) {} }\n }\n java.refreshExplore();\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\n\n\n \n \n 书源更新\n \n \n \n\n\n
\n
\n\n
\n
\n
正在检查更新...
\n
\n\n
\n
\n
\n

光遇书源更新

\n

推荐使用阅读Sigma版本
正式版可能存在兼容性问题-下载

\n
\n\n
\n
\n
待检查
\n

当前版本

\n
-
\n
您的当前版本
\n
\n\n
\n \n
\n\n
\n
最新版本
\n

最新版本

\n
-
\n
可用最新版本
\n
\n
\n\n
\n
\n\n
\n
\n
\n

最新更新

\n
最新发布
\n
\n
\n
\n
\n
\n
\n
\n\n
\n\n
\n
\n

历史更新

\n \n
\n
\n
\n
\n
\n\n \n\n\n`;\n java.startBrowser(`data:text/html;base64,${java.base64Encode(html)}`, '光遇书源更新');\n}\n\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\n\n\n\n\n光遇小说 - 线路设置\n\n\n\n\n
\n
\n
\n
\n \n 线路选择\n
\n
选择最快的服务器线路以获得最佳体验
\n
\n \n \n
\n

设置完成后请点击右上角的√应用设置

\n
\n \n
\n
\n 当前选中: --\n
\n
\n \n
\n \n 最后检测: --:--:--\n \n
\n \n
\n
\n
失败自动切换
\n
当前线路失败时自动尝试切换到其他可用线路
\n
\n \n
\n \n
\n
\n
超时自动切换
\n
请求超时后自动切换到其他可用线路
\n
\n
\n \n \n
\n
\n \n
\n
\n
切换提醒弹窗
\n
线路切换时显示提醒弹窗通知
\n
\n \n
\n \n
\n \n
\n ${data || hostsbk[0]}\n ${this.getVariable('自动切换') || 'true'}\n ${this.getVariable('超时自动切换') || 'true'}\n ${this.getVariable('超时时间') || '10'}\n ${this.getVariable('切换提醒') || 'false'}\n
\n
\n
\n\n\n\n\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 = '10';\n let notifySwitch = 'false';\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') || '10';\n notifySwitch = getSpanText('notifySwitchValue') || 'false';\n } catch (e) {\n host = hostsbk[0];\n autoSwitch = 'true';\n timeoutSwitch = 'true';\n timeout = '10';\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\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 let base_url = this.BaseUrl();\n let qttoken = this.getToken();\n\n let html = `\n\n\n\n\n\n光遇小说 - 段评设置\n\n\n\n\n
\n
\n
\n

设置完成后请点击右上角的√应用设置

\n
\n \n
\n
\n
段评
\n
仅 番茄、七猫、书旗、塔读、QQ阅读 支持
\n
\n \n
\n \n
\n
\n
神评论
\n
需要开启段评开关才可生效,仅支持 番茄、七猫、书旗
\n
\n \n
\n \n
\n
\n
本章说
\n
仅支持 番茄、七猫、书旗
\n
\n \n
\n \n
\n
\n
气泡增大
\n
开启后,气泡显示会加大一些
\n
\n \n
\n \n
\n
\n
段评样式
\n
选择段评显示的样式风格,下方可实时预览效果
\n
\n
\n \n
\n
\n \n
\n
\n
边框颜色
\n
设置段评边框颜色
\n
\n
\n \n \n
\n
\n \n
\n
\n
字体颜色
\n
设置段评字体颜色
\n
\n
\n \n \n
\n
\n \n
\n
\n
填充颜色
\n
设置段评背景填充颜色
\n
\n
\n \n \n \n
\n
\n \n \n
\n
\n
\n
自定义样式管理
\n
点击下方\"+\"添加新的自定义样式,支持自定义名称
\n
\n
\n \n \n \n
\n
\n \n
\n
\n
\n \n \n
\n
\n \n 实时预览\n
\n
\n
\n
\n \n \n
\n
\n ${(data && data.段评开关) || 'true'}\n ${(data && data.神评论开关) || 'true'}\n ${(data && data.本章说开关) || 'true'}\n ${(data && data.气泡增大) || 'false'}\n ${(data && data.段评样式) || '0'}\n ${(data && data.段评边框颜色) || '#A7A7A7'}\n ${(data && data.段评字体颜色) || '#A7A7A7'}\n ${(data && data.段评填充颜色) || 'transparent'}\n ${base_url}\n ${qttoken}\n
\n \n
\n \n \n
\n
\n\n\n\n\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(']*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('shenpingSwitchValue'),\n '本章说开关': getSpanText('benzhangSwitchValue'),\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 '神评论开关': 'true',\n '本章说开关': 'true',\n '气泡增大': 'false',\n '段评样式': '0',\n '段评边框颜色': '#A7A7A7',\n '段评字体颜色': '#A7A7A7',\n '段评填充颜色': 'transparent',\n '自定义段评样式': {},\n };\n }\n this.setVariable(\"段评设置\", settings, 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 \n\n\n\n\n光遇小说 - 管理中心\n\n\n\n\n
\n
\n
\n

设置完成后请点击右上角的√应用设置

\n
\n \n
\n
书源设置
\n
搜索设置
\n
\n \n
\n
\n
\n
搜索模式
\n
切换后会搜索对应的类型,不推荐手动设置,可以在搜索的时候使用关键词:t:书名 搜听书,d:书名 搜短剧,m:书名 搜漫画
\n
\n
\n \n
\n
\n \n
\n
\n
显示图片
\n
关闭后,文章内的图片将会清除
\n
\n \n
\n \n
\n
\n
完整简介
\n
关闭后,书籍详情页会精简简介内容
\n
\n \n
\n \n
\n
\n
书籍讨论
\n
仅支持番茄、七猫,需要魔改版本阅读
\n
\n \n
\n \n
\n
\n
同步书架
\n
开启后,刷新书籍详情页会将书架书籍同步到光遇网站网页端书架
\n
\n \n
\n \n
\n
\n
同步弹窗
\n
弹窗开关可以让您知晓同步状态,关闭后不会弹窗
\n
\n \n
\n \n
\n
\n
网络模式
\n
开启后部分来源会使用本地网络请求,如69书吧,可能需要开梯子访问,不推荐开启
\n
\n
\n \n
\n
\n \n
\n
\n
强制搜索
\n
开启后搜索时会 强制搜索全部来源,搜索时长会 明显增长,若无必要不建议开启
\n
\n \n
\n \n
\n
\n
目录显示来源
\n
开启后,目录最后一个章节的标题将显示来源,方便换源的时候开启加载目录可以知道是哪个来源
\n
\n \n
\n
\n \n
\n
\n
\n \n 小说\n
\n
\n \n 听书\n
\n
\n \n 漫画\n
\n
\n \n 短剧\n
\n
\n
\n 推荐选择全部,如需要搜索特定的资源可以搜索的时候使用 书名@来源 进行搜索\n
\n
\n
\n 当前选中: 0 个来源\n
\n
\n \n
\n \n
\n ${(data && data.小说) || '全部'}\n ${(data && data.听书) || '全部'}\n ${(data && data.漫画) || '全部'}\n ${(data && data.短剧) || '全部'}\n ${(data && data.搜索模式) || '小说'}\n ${(data && data.显示图片) || 'true'}\n ${(data && data.完整简介) || 'true'}\n ${(data && data.书籍讨论) || 'false'}\n ${(data && data.同步书架) || 'false'}\n ${(data && data.同步弹窗) || 'true'}\n ${(data && data.网络模式) || '服务器'}\n ${(data && data.强制搜索) || 'false'}\n ${(data && data.目录显示来源) || 'true'}\n
\n
\n
\n
\n\n\n\n\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('bookDiscussionSwitchValue'),\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 '同步书架': 'false',\n '同步弹窗': 'true',\n '网络模式': '服务器',\n '强制搜索': 'false',\n '目录显示来源': 'true',\n '小说': '全部',\n '听书': '全部',\n '漫画': '全部',\n '短剧': '全部',\n };\n }\n this.setVariable(\"更多设置\", settings, false)\n}\n", "lastUpdateTime": 1791479867070, "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\": \"saveSettings()\",\n \"style\": {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4\n }\n }, {\n \"name\": \" 📨从云端获取设置\",\n \"type\": \"button\",\n \"action\": \"getSettings()\",\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 \n source.putLoginInfo(JSON.stringify(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('https://vip.qingtian618.com', '光遇看书');\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\nfunction saveSettings() {\n\t java.longToast(`\\n正在保存~`);\n let moreSettings = getVariable(\"更多设置\") || {};\n let body_more = {\n \"legado_more\":moreSettings\n }\n\tlet data_more = request('/user_settings','POST',body_more)\n const response_more = JSON.parse(data_more);\n let paraSettings = getVariable(\"段评设置\") || {};\n let body_para = {\n \"legado_para\":paraSettings\n }\n\tlet data_para = request('/user_settings','POST',body_para)\n const response_para = JSON.parse(data_para);\n java.longToast(`\\n书源设置:${response_more.msg}\\n段评设置:${response_para.msg}`);\n\t}\n \n function getSettings() {\n \tjava.longToast(`\\n正在获取~`);\n let more = false;\n let para = false;\n let data_more = request('/user_settings?set_type=legado_more','GET');\n const response_more = JSON.parse(data_more);\n if (response_more.data) {\n setVariable(\"更多设置\",response_more.data)\n more = true;\n }\n let data_para = request('/user_settings?set_type=legado_para','GET');\n const response_para = JSON.parse(data_para);\n if (response_para.data) {\n setVariable(\"段评设置\",response_para.data)\n para=true;\n }\n java.longToast(`\\n书源设置:${more?'应用成功':'应用失败'}\\n段评设置:${para?'应用成功':'应用失败'}`);\n }", "respondTime": 180000, "ruleBookInfo": { "author": "$.author", "canReName": "1", "coverUrl": "$.thumb_url", "init": "\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$.data", "intro": "\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['网络模式'] || '服务器') == \"本地\" ? \"本地网络\" : \"服务器网络\";\nconst discussion = moreSettings && moreSettings['书籍讨论'] || 'false';\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‎${isValid(abstract) ? `📖 书籍简介:\\n${formatAbstract(abstract)}\\n${heavyDivider}` : ''}\n🏷 数据来源:${sources}\n${simplifiedBasicInfo}`;\n\n} else {\n info = info.split(\"\\n\").map(line => line.replace(/^ {4}/, \"\")).join(\"\\n\");\n}\nif (discussion=='true' && tab == '小说' && (sources=='番茄'||sources=='七猫'||sources.replace('svip_','')=='书旗')) {\n info = info.split(\"\\n\").map(line => line.replace(/^ {4}/, \"\")).join(\"
\");\n\tlet discussionUrl = `${BaseUrl()}/book_info?book_id=${book_id}&source=${sources}`;\n`${buildDiscussionButton(discussionUrl,sources)}
${info}
`\n}\n
", "lastChapter": "{{$.source}} {{$.last_chapter_title}} {{$.last_chapter_update_time}}", "name": "$.book_name", "tocUrl": "\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", "wordCount": "$.word_number" }, "ruleContent": { "content": "\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阅读','书旗','svip_书旗'];\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\t//book_order = 2;\n \t\treturn;\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 godop = dpSettings && dpSettings['神评论开关'] || 'true';\nlet endop = dpSettings && dpSettings['本章说开关'] || 'true';\nlet show_img = moreSettings && moreSettings['显示图片'] || 'true';\n\n\nlet god = false;\n\nif ((sources == \"番茄\" || sources == '七猫' || sources=='svip_书旗') && godop == 'true' && tab == \"小说\") {\n\tgod = true\n\t}\nlet params = {\n html,\n item_id,\n source: sources,\n tab,\n tone_id,\n variable: varia,\n version: VERSION,\n god\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\nlet fqssionid = getFqToken();\nlet deviceType = '安卓';\n try {\n \tjava.deviceID();\n \tdeviceType = '苹果'\n \t} catch {}\nif (hasReview) {\n content = content\n .replace(/ident=\"/g, `ident=\"${base_url}`)\n .replace(/book_id=/g, `book_id=${book_id}&ssionid=${fqssionid}`);\n \n content = deviceType == '苹果' \n ? paraForiOS(content, sources) \n : paraForAndroid(content, sources);\n}\nif ((sources == \"番茄\" || sources == \"七猫\"||sources.replace('svip_','')=='书旗') && endop == 'true' && tab == \"小说\") {\n let svg = `${base_url}/chapter_review_svg?book_id=${book_id}&item_id=${item_id}&source=${sources}&ssionid=${fqssionid}`;\n\tlet eurl = `${base_url}/get_review?book_id=${book_id}&item_id=${item_id}&source=${sources}`;\n if (deviceType == '苹果') {\n let chapterSvg = java.ajax(svg);\n svg = 'data:image/svg+xml;base64,'+java.base64Encode(chapterSvg);\n } else {\n svg += '&img=1'\n }\n let imgSvg = `\\n`;\n if (islyc == '苹果') {\n \timgSvg = `\\n
`;\n \t}\n\tcontent = content+ imgSvg;\n\t}\ndata = JSON.stringify({ content });\n\nlet isSpecialSource = isSpecialMedia(sources);\nlet isDramaTab = tab == '短剧';\nif (islyc != '苹果' && (isDramaTab || isSpecialSource)) {\n\tjava.log(isSpecialSource)\n if (!java.getAppVariant || isSpecialSource) {\n data = JSON.stringify({\n content: `【右上角刷新】开启播放(下一集请切换下一章刷新)\\n播放直链:\\n${content}`\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 (isSpecialSource) {\n video_url = content;\n }\n java.startBrowser(video_url, title);\n java.toast('正在加载视频...');\n }\n \n }\n\n\n}\n//java.log(data)\ndata;\n
$.content", "title": "@js:\nresult = String(java.hexDecodeToString(result))\nJSON.parse(result)['title'].replace(JSON.parse(result)['sources']+':','')" }, "ruleExplore": { "author": "$.author", "bookList": "$.data", "bookUrl": "\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", "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": "\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\n$.data", "bookUrl": "\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", "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": "\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();\n\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 \"备用\": 8,\n // 墨听:运行时恢复短剧媒体容器,但避免源级静态扫描直接命中视频类型常量\n default: java.getAppVariant && !isSpecialMedia(sources) ? (2 * 2) : 8\n }\n};\nconst currentType = typeMap[tab] || typeMap[\"小说\"];\nif (device_type=='苹果') {\nbook.type = currentType[device_type];\n} else {\nbook.type = currentType.default;\n}\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$.data", "chapterName": "$.title", "chapterUrl": "\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", "updateTime": "$.first_pass_time" }, "searchUrl": "\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", "weight": 0 } ]