Compare commits
2
Commits
af9004adaf
...
221343fc72
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
221343fc72 | ||
|
|
3768ec0a31 |
No files matched your search
+3
-1
@@ -32,8 +32,10 @@ SRC_DIR="/root/code/novel/fanqie-go"
|
||||
DOMAIN="fq.20010117.xyz"
|
||||
|
||||
# ---------- 证书 ----------
|
||||
# Certbot 申请证书的邮箱,用于接收过期提醒
|
||||
# acme.sh 申请证书的邮箱,用于账户注册与过期提醒
|
||||
CERT_EMAIL="you@example.com"
|
||||
# 证书安装目录(acme.sh --install-cert 目标,nginx 配置从此处读取)
|
||||
# CERT_DIR="/etc/nginx/ssl"
|
||||
|
||||
# ---------- 编译 ----------
|
||||
# BUILD_TARGET="./cmd/fanqie"
|
||||
|
||||
@@ -57,3 +57,4 @@ fanqie-*
|
||||
.env
|
||||
.finesse/
|
||||
fanqie.log
|
||||
.codegraph/
|
||||
@@ -40,6 +40,7 @@ NGINX_CONF="${NGINX_CONF:-/etc/nginx/conf.d/${APP_NAME}.conf}"
|
||||
APP_PORT="${APP_PORT:-40180}" # 程序监听端口
|
||||
DOMAIN="${DOMAIN:-fq.20010117.xyz}" # 域名
|
||||
CERT_EMAIL="${CERT_EMAIL:-admin@${DOMAIN}}" # 证书申请邮箱(过期提醒)
|
||||
CERT_DIR="${CERT_DIR:-/etc/nginx/ssl}" # 证书目录(acme.sh --install-cert 安装位置)
|
||||
BUILD_TARGET="${BUILD_TARGET:-./cmd/fanqie}" # go build 的包路径
|
||||
ENV_FILE="${ENV_FILE:-${SRC_DIR}/.env}" # 应用环境变量(.env.example 为模板)
|
||||
KEEP_VERSIONS="${KEEP_VERSIONS:-5}" # 保留最近几个版本
|
||||
@@ -245,8 +246,8 @@ restart_and_verify() {
|
||||
|
||||
|
||||
# 写入 nginx 配置(--init 与 --sync-nginx 共用的唯一模板来源)
|
||||
# 模板自带完整 443/证书配置(证书存在时启用),不依赖 certbot --nginx 注入——
|
||||
# 这样 --sync-nginx 覆盖配置时不会丢失 HTTPS 段(certbot 注入式配置会被覆盖回 80-only)
|
||||
# 模板自带完整 443/证书配置(证书存在时启用),不依赖签发工具注入——
|
||||
# 这样 --sync-nginx 覆盖配置时不会丢失 HTTPS 段(注入式配置会被覆盖回 80-only)
|
||||
write_nginx_conf() {
|
||||
# 反代 location(80 无证书时与 443 共用)
|
||||
local proxy_locations
|
||||
@@ -281,7 +282,7 @@ LOC_EOF
|
||||
)
|
||||
|
||||
local ssl_block=""
|
||||
if [ -f "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" ]; then
|
||||
if [ -f "${CERT_DIR}/${DOMAIN}/fullchain.pem" ]; then
|
||||
log "检测到 ${DOMAIN} 证书,配置启用 443"
|
||||
ssl_block=$(cat <<SSL_EOF
|
||||
|
||||
@@ -289,8 +290,8 @@ server {
|
||||
listen 443 ssl http2;
|
||||
server_name ${DOMAIN};
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem;
|
||||
ssl_certificate ${CERT_DIR}/${DOMAIN}/fullchain.pem;
|
||||
ssl_certificate_key ${CERT_DIR}/${DOMAIN}/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
|
||||
access_log /var/log/nginx/${APP_NAME}.access.log;
|
||||
@@ -300,7 +301,7 @@ ${proxy_locations}
|
||||
SSL_EOF
|
||||
)
|
||||
else
|
||||
warn "未找到 /etc/letsencrypt/live/${DOMAIN}/ 证书,本次仅写 80 配置(不跳转 HTTPS);证书签发后重跑: $0 --sync-nginx"
|
||||
warn "未找到 ${CERT_DIR}/${DOMAIN}/ 证书,本次仅写 80 配置(不跳转 HTTPS);证书签发后重跑: $0 --sync-nginx"
|
||||
fi
|
||||
|
||||
# 80 端口:有证书时仅 ACME 验证 + 301 跳转;无证书时直接反代
|
||||
@@ -321,7 +322,7 @@ server {
|
||||
access_log /var/log/nginx/${APP_NAME}.access.log;
|
||||
error_log /var/log/nginx/${APP_NAME}.error.log;
|
||||
|
||||
# ACME http-01 验证(certbot webroot 模式依赖)
|
||||
# ACME http-01 验证(acme.sh webroot 模式依赖)
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/html;
|
||||
}
|
||||
@@ -506,36 +507,31 @@ EOF
|
||||
write_nginx_conf
|
||||
|
||||
|
||||
# ---- Certbot 续期后自动重载 Nginx(deploy-hook 只在续期成功时执行)----
|
||||
HOOK_FILE="/etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh"
|
||||
if [ ! -f "${HOOK_FILE}" ]; then
|
||||
log "创建 Certbot 续期重载 Hook: ${HOOK_FILE}"
|
||||
$SUDO mkdir -p /etc/letsencrypt/renewal-hooks/deploy/
|
||||
$SUDO tee "${HOOK_FILE}" >/dev/null <<'HOOK'
|
||||
#!/bin/bash
|
||||
if /usr/sbin/nginx -t 2>/dev/null; then
|
||||
systemctl reload nginx
|
||||
fi
|
||||
HOOK
|
||||
$SUDO chmod +x "${HOOK_FILE}"
|
||||
fi
|
||||
|
||||
# ---- 首次初始化自动申请 HTTPS 证书(一次性;续期由 certbot.timer 自动处理)----
|
||||
if [ ! -d "/etc/letsencrypt/live/${DOMAIN}" ]; then
|
||||
if command -v certbot >/dev/null 2>&1; then
|
||||
log "未检测到证书,尝试自动申请: ${DOMAIN}"
|
||||
$SUDO mkdir -p /var/www/html
|
||||
if $SUDO certbot certonly --webroot -w /var/www/html -d "${DOMAIN}" --non-interactive --agree-tos -m "${CERT_EMAIL}"; then
|
||||
log "证书申请成功,写入 443 配置..."
|
||||
write_nginx_conf
|
||||
else
|
||||
warn "证书申请失败,请检查域名解析与 80 端口;成功后重跑: $0 --sync-nginx"
|
||||
fi
|
||||
else
|
||||
warn "未安装 certbot,跳过证书申请。可手动执行: dnf install certbot python3-certbot-nginx"
|
||||
fi
|
||||
else
|
||||
# ---- 首次初始化自动申请 HTTPS 证书(一次性;续期由 acme.sh cron 自动处理)----
|
||||
if [ -f "${CERT_DIR}/${DOMAIN}/fullchain.pem" ]; then
|
||||
log "证书已存在,跳过申请"
|
||||
else
|
||||
# 定位 acme.sh:PATH → 当前用户目录 → root 目录(sudo 后 HOME 可能被重置)
|
||||
ACME_SH="$(command -v acme.sh 2>/dev/null || true)"
|
||||
for c in "${HOME}/.acme.sh/acme.sh" "/root/.acme.sh/acme.sh"; do
|
||||
if [ -z "$ACME_SH" ] && [ -x "$c" ]; then ACME_SH="$c"; fi
|
||||
done
|
||||
if [ -z "$ACME_SH" ]; then
|
||||
err "未找到 acme.sh,请先安装: curl https://get.acme.sh | sh -s email=${CERT_EMAIL}"
|
||||
exit 1
|
||||
fi
|
||||
log "未检测到证书,使用 acme.sh 申请: ${DOMAIN}"
|
||||
$SUDO mkdir -p /var/www/html "${CERT_DIR}/${DOMAIN}"
|
||||
if $SUDO "$ACME_SH" --issue -d "${DOMAIN}" -w /var/www/html --server letsencrypt --keylength ec-256 -m "${CERT_EMAIL}" \
|
||||
&& $SUDO "$ACME_SH" --install-cert -d "${DOMAIN}" --ecc \
|
||||
--fullchain-file "${CERT_DIR}/${DOMAIN}/fullchain.pem" \
|
||||
--key-file "${CERT_DIR}/${DOMAIN}/privkey.pem" \
|
||||
--reloadcmd "systemctl reload nginx"; then
|
||||
log "证书申请成功,写入 443 配置..."
|
||||
write_nginx_conf
|
||||
else
|
||||
warn "证书申请失败,请检查域名解析与 80 端口;成功后重跑: $0 --sync-nginx"
|
||||
fi
|
||||
fi
|
||||
|
||||
if command -v getenforce >/dev/null 2>&1 && [ "$(getenforce)" != "Disabled" ]; then
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
[
|
||||
{
|
||||
"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": "<js>\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(\"番茄登录过期Line truncated
|
||||
"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 <button style=\"\">💬 书籍讨论@onclick:${clickAction}</button>\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(\"更多设置\") || {};Line truncated
|
||||
"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\": \"🎨搜索漫Line truncated
|
||||
"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)}Line truncated
|
||||
"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['网络模式'] || '服务器') == \"本地\" ? \"本地网络\" : \"服务器网络\";\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(\"<br>\");\n\tlet discussionUrl = `${BaseUrl()}/book_info?book_id=${book_id}&source=${sources}`;\n`<usehtml>${buildDiscussionButton(discussionUrl,sources)}<br>${info}</usehtml>`\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阅读','书旗','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 goLine truncated
|
||||
"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();\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 javaLine truncated
|
||||
"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
|
||||
}
|
||||
]
|
||||
Reference in new issue
Block a user