🅿️ Pixiv 小说

https://www.pixiv.net/novel

DowneyRem (9968)19小时前

Pixiv 小说(更新📆2025-11-22

⚠️ 首次需要代理,需要登录

✅ 后续可以直连,需要登录

✅ 兼容阅读 Beta版:3.25.0527 起

✅ 兼容源阅:当前最新版本1.0 (112)


书源版本:246
使用说明:📌阅读版本 3.25.0527 及之后版本可用
可用功能:搜索发现添加网址订阅源
搜索小说:单篇系列标签作者
发现小说:关注追更推荐发现
发现小说:收藏书签首页排行
添加网址:小说系列作者
订阅用法:点击订阅源打开小说/系列小说,【刷新】,点击【加入书架】按钮,添加到书架

书源发布:Pixiv 书源频道 https://t.me/PixivSource
项目地址:https://github.com/DowneyRem/PixivSource
使用教程:https://github.com/DowneyRem/PixivSource/blob/main/doc/Pixiv.md

旧版书源:
https://cdn.jsdelivr.net/gh/DowneyRem/PixivSource@191/pixiv.json
https://raw.githubusercontent.com/DowneyRem/PixivSource/191/pixiv.json

规则订阅:import 订阅源
https://cdn.jsdelivr.net/gh/DowneyRem/PixivSource@main/import.json
https://raw.githubusercontent.com/DowneyRem/PixivSource/main/import.json
⚙️ 书源设置:
设置1️⃣:打开小说 - 菜单 - 登录 - 点击下方按钮
设置2️⃣:编辑书源 - 基本 - 变量说明 - 修改并保存

🚫 屏蔽作者(本地)
▶️设置方法:打开小说 - 菜单 - 登录 - 🚫 屏蔽作者

🚫 屏蔽标签/描述(本地)
1️⃣编辑书源:菜单 - 登录 - 点击【 👀 查看屏蔽】
2️⃣切换列表:点击按钮,切换至【相应屏蔽列表】
3️⃣输入内容:在【输入内容】处输入屏蔽内容
4️⃣屏蔽作者:点击【🚫 加入屏蔽】,屏蔽该内容

📌 喜欢标签(本地)
1️⃣编辑书源:菜单 - 登录 ,找到输入内容
2️⃣输入内容:输入标签,点击【📌 喜欢标签】

❤️ 查看他人收藏:
1️⃣编辑书源:菜单 - 登录 ,找到输入内容
2️⃣输入内容:输入作者ID,点击【❤️ 他人收藏】
二维码导入
{
    "bookSourceComment": "Pixiv 小说(更新📆:2025-11-22)\n\n书源版本:246\n使用说明:📌阅读版本 3.25.0527 及之后版本可用\n可用功能:✅搜索✅发现✅添加网址✅订阅源\n搜索小说:✅单篇✅系列✅标签✅作者\n发现小说:✅关注✅追更✅推荐✅发现\n发现小说:✅收藏✅书签✅首页✅排行\n添加网址:✅小说✅系列✅作者\n订阅用法:点击订阅源打开小说\/系列小说,【刷新】,点击【加入书架】按钮,添加到书架\n\n书源发布:Pixiv 书源频道 https:\/\/t.me\/PixivSource\n项目地址:https:\/\/github.com\/DowneyRem\/PixivSource\n使用教程:https:\/\/github.com\/DowneyRem\/PixivSource\/blob\/main\/doc\/Pixiv.md\n\n旧版书源:\nhttps:\/\/cdn.jsdelivr.net\/gh\/DowneyRem\/PixivSource@191\/pixiv.json\nhttps:\/\/raw.githubusercontent.com\/DowneyRem\/PixivSource\/191\/pixiv.json\n\n规则订阅:import 订阅源\nhttps:\/\/cdn.jsdelivr.net\/gh\/DowneyRem\/PixivSource@main\/import.json\nhttps:\/\/raw.githubusercontent.com\/DowneyRem\/PixivSource\/main\/import.json\n\n⚙️ 书源设置:\n设置1️⃣:打开小说 - 菜单 - 登录 - 点击下方按钮\n设置2️⃣:编辑书源 - 基本 - 变量说明 - 修改并保存\n\n🚫 屏蔽作者(本地):\n▶️设置方法:打开小说 - 菜单 - 登录 - 🚫 屏蔽作者\n\n🚫 屏蔽标签\/描述(本地):\n1️⃣编辑书源:菜单 - 登录 - 点击【 👀 查看屏蔽】\n2️⃣切换列表:点击按钮,切换至【相应屏蔽列表】\n3️⃣输入内容:在【输入内容】处输入屏蔽内容\n4️⃣屏蔽作者:点击【🚫 加入屏蔽】,屏蔽该内容\n\n📌 喜欢标签(本地):\n1️⃣编辑书源:菜单 - 登录 ,找到输入内容\n2️⃣输入内容:输入标签,点击【📌 喜欢标签】\n\n❤️ 查看他人收藏:\n1️⃣编辑书源:菜单 - 登录 ,找到输入内容\n2️⃣输入内容:输入作者ID,点击【❤️ 他人收藏】",
    "bookSourceGroup": "🔞 Pixiv",
    "bookSourceName": "🅿️ Pixiv 小说",
    "bookSourceType": 0,
    "bookSourceUrl": "https:\/\/www.pixiv.net\/novel",
    "bookUrlPattern": "(https?:\/\/)?(www\\.)?pixiv\\.net(\/ajax)?\/(novel\/(show\\.php\\?id=|series\/)?|users?\/)\\d+.*",
    "concurrentRate": "3\/2000",
    "customButton": false,
    "customOrder": 0,
    "enabled": true,
    "enabledCookieJar": true,
    "enabledExplore": true,
    "eventListener": false,
    "exploreUrl": "@js:\nlet SHOW_R18_GENRE, SHOW_GENERAL_NEW, SHOW_GENERAL_RANK, SHOW_GENERAL_GENRE\ntry {\n    settings = JSON.parse(String(source.variableComment).match(RegExp(\/{([\\s\\S]*?)}\/gm)))\n    SHOW_R18_GENRE = settings.SHOW_R18_GENRE         \/\/ 发现:热门分类显示R18小说\n    SHOW_GENERAL_NEW = settings.SHOW_GENERAL_NEW     \/\/ 发现:最新、企划、约稿显示一般小说\n    SHOW_GENERAL_RANK = settings.SHOW_GENERAL_RANK   \/\/ 发现:排行榜显示一般小说\n    SHOW_GENERAL_GENRE = settings.SHOW_GENERAL_GENRE \/\/ 发现:热门分类显示一般小说\n} catch (e) {\n    SHOW_R18_GENRE = false\n    SHOW_GENERAL_NEW = false\n    SHOW_GENERAL_RANK = false\n    SHOW_GENERAL_GENRE = false\n}\n\nli = [\n    {\"⭐️ 关注\": \"https:\/\/www.pixiv.net\/ajax\/follow_latest\/novel?p={{page}}&mode=r18&lang=zh\"},\n    {\"📃 追更\": \"https:\/\/www.pixiv.net\/ajax\/watch_list\/novel?p={{page}}&new=1&lang=zh\"},\n    {\"💯 推荐\": \"https:\/\/www.pixiv.net\/ajax\/top\/novel?mode=r18&lang=zh\"},\n    {\"🔍 发现\": \"https:\/\/www.pixiv.net\/ajax\/novel\/discovery?mode=r18&lang=zh\"},\n    {\"❤️ 收藏\": \"https:\/\/www.pixiv.net\/ajax\/user\/{{cache.get(\\\"pixiv:uid\\\")}}\/novels\/bookmarks?tag=&offset={{(page-1)*24}}&limit=24&rest=show&lang=zh\"},\n    {\"㊙️ 收藏\": \"https:\/\/www.pixiv.net\/ajax\/user\/{{cache.get(\\\"pixiv:uid\\\")}}\/novels\/bookmarks?tag=&offset={{(page-1)*24}}&limit=24&rest=hide&lang=zh\"},\n    {\"🏷️ 书签\": \"https:\/\/www.pixiv.net\/novel\/marker_all.php\"},\n    {\"🏠 首页\": \"https:\/\/www.pixiv.net\"},\n]\n\nnormal = [\n    {\"✅ 常规 小说 推荐 ✅\": \"\"},\n    {\"⭐️ 关注\": \"https:\/\/www.pixiv.net\/ajax\/follow_latest\/novel?p={{page}}&mode=all&lang=zh\"},\n    {\"💯 推荐\": \"https:\/\/www.pixiv.net\/ajax\/top\/novel?mode=all&lang=zh\"},\n    {\"🔍 发现\": \"https:\/\/www.pixiv.net\/ajax\/novel\/discovery?mode=safe&lang=zh\"},\n    {\"🆙 更新\": \"https:\/\/cdn.jsdelivr.net\/gh\/DowneyRem\/PixivSource@main\/pixiv.json\"},\n]\n\nr18New = [\n    {\"🆕 最新 企划 约稿 💰\": \"\"},\n    {\"🆕 最新\": \"https:\/\/www.pixiv.net\/ajax\/novel\/new?lastId=0&limit=20&r18=true&lang=zh\"},\n    {\"📑 企划\": \"https:\/\/www.pixiv.net\/ajax\/user_event\/portal\/novels?mode=r18&p={{page}}&lang=zh\"},\n    {\"💰 约稿\": \"https:\/\/www.pixiv.net\/ajax\/commission\/page\/request\/complete\/novels?mode=r18&p={{page}}&lang=zh\"},\n    {\"🔍 发现\": \"https:\/\/www.pixiv.net\/ajax\/novel\/discovery?mode=all&lang=zh\"},\n]\n\ngeneralNew = [\n    {\"✅ 最新 企划 约稿 ✅\": \"\"},\n    {\"最新\": \"https:\/\/www.pixiv.net\/ajax\/novel\/new?lastId=0&limit=20&r18=false&lang=zh\"},\n    {\"企划\": \"https:\/\/www.pixiv.net\/ajax\/user_event\/portal\/novels?mode=all&p={{page}}&lang=zh\"},\n    {\"约稿\": \"https:\/\/www.pixiv.net\/ajax\/commission\/page\/request\/complete\/novels?mode=all&p={{page}}&lang=zh\"},\n    {\"编辑\": \"https:\/\/www.pixiv.net\/novel\/editors_picks\"},\n]\n\nr18Rank = [\n    {\"👑 排行榜单 👑\": \"\"},\n    {\"今日\": \"https:\/\/www.pixiv.net\/novel\/ranking.php?mode=daily_r18\"},\n    {\"本周\": \"https:\/\/www.pixiv.net\/novel\/ranking.php?mode=weekly_r18\"},\n    {\"R18G\": \"https:\/\/www.pixiv.net\/novel\/ranking.php?mode=r18g\"},\n    {\"男性\": \"https:\/\/www.pixiv.net\/novel\/ranking.php?mode=male_r18\"},\n    {\"女性\": \"https:\/\/www.pixiv.net\/novel\/ranking.php?mode=female_r18\"}\n]\n\ngeneralRank = [\n    {\"🏆 排行榜单 🏆\": \"\"},\n    {\"今日\": \"https:\/\/www.pixiv.net\/novel\/ranking.php?mode=daily\"},\n    {\"本周\": \"https:\/\/www.pixiv.net\/novel\/ranking.php?mode=weekly\"},\n    {\"本月\": \"https:\/\/www.pixiv.net\/novel\/ranking.php?mode=monthly\"},\n    {\"男性\": \"https:\/\/www.pixiv.net\/novel\/ranking.php?mode=male\"},\n    {\"女性\": \"https:\/\/www.pixiv.net\/novel\/ranking.php?mode=female\"},\n    {\"新人\": \"https:\/\/www.pixiv.net\/novel\/ranking.php?mode=rookie\"},\n    {\"原创\": \"https:\/\/www.pixiv.net\/novel\/ranking.php?mode=weekly_original\"},\n    {\"AI生成\": \"https:\/\/www.pixiv.net\/novel\/ranking.php?mode=weekly_ai\"}\n]\n\nr18Genre = [\n    {\"🔥 原创热门 🔥\": \"\"},\n    {\"男性\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/male?mode=r18&lang=zh\"},\n    {\"女性\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/female?mode=r18&lang=zh\"},\n    {\"恋爱\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/romance?mode=r18&lang=zh\"},\n    {\"异世界奇幻\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/isekai_fantasy?mode=r18&lang=zh\"},\n    {\"现代奇幻\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/contemporary_fantasy?mode=r18&lang=zh\"},\n    {\"悬疑\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/mystery?mode=r18&lang=zh\"},\n    {\"恐怖\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/horror?mode=r18&lang=zh\"},\n    {\"科幻\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/sci-fi?mode=r18&lang=zh\"},\n    {\"文学\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/literature?mode=r18&lang=zh\"},\n    {\"情感\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/drama?mode=r18&lang=zh\"},\n    {\"历史\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/historical_pieces?mode=r18&lang=zh\"},\n    {\"耽美\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/bl?mode=r18&lang=zh\"},\n    {\"百合\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/yuri?mode=r18&lang=zh\"},\n    {\"散文·诗歌\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/poetry?mode=r18&lang=zh\"},\n    {\"随笔·纪实\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/non-fiction??mode=r18&lang=zh\"},\n    {\"剧本\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/screenplays?mode=r18&lang=zh\"},\n    {\"评论\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/reviews?mode=r18&lang=zh\"},\n    {\"其他\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/other?mode=r18&lang=zh\"}\n]\n\ngeneralgGenre = [\n    {\"❤️‍🔥 原创热门 ❤️‍🔥\": \"\"},\n    {\"综合\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/all?mode=safe&lang=zh\"},\n    {\"恋爱\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/romance?mode=safe&lang=zh\"},\n    {\"异世界奇幻\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/isekai_fantasy?mode=safe&lang=zh\"},\n    {\"现代奇幻\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/contemporary_fantasy?mode=safe&lang=zh\"},\n    {\"悬疑\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/mystery?mode=safe&lang=zh\"},\n    {\"恐怖\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/horror?mode=safe&lang=zh\"},\n    {\"科幻\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/sci-fi?mode=safe&lang=zh\"},\n    {\"文学\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/literature?mode=safe&lang=zh\"},\n    {\"情感\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/drama?mode=safe&lang=zh\"},\n    {\"历史\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/historical_pieces?mode=safe&lang=zh\"},\n    {\"耽美\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/bl?mode=safe&lang=zh\"},\n    {\"百合\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/yuri?mode=safe&lang=zh\"},\n    {\"散文·诗歌\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/poetry?mode=safe&lang=zh\"},\n    {\"随笔·纪实\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/non-fiction??mode=safe&lang=zh\"},\n    {\"剧本\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/screenplays?mode=safe&lang=zh\"},\n    {\"评论\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/reviews?mode=safe&lang=zh\"},\n    {\"其他\": \"https:\/\/www.pixiv.net\/ajax\/genre\/novel\/other?mode=safe&lang=zh\"}\n]\n\nlet likeTagLinks = [{\"📌 喜欢标签 📌\":\"\"}]\nlet othersBookmarks = [{\"❤️ 他人收藏 ❤️\": \"\"}]\n\nli = li.concat(normal)\nli = li.concat(r18New)\nif (SHOW_GENERAL_NEW === true) {\n    li = li.concat(generalNew)\n}\nli = li.concat(r18Rank)\nif (SHOW_GENERAL_RANK === true) {\n    li = li.concat(generalRank)\n}\nif (SHOW_R18_GENRE === true) {\n    li = li.concat(r18Genre)\n}\nif (SHOW_GENERAL_GENRE === true) {\n    li = li.concat(generalgGenre)\n}\nsleepToast('使用指南🔖\\n\\n发现 - 更新 - 点击\"🔰 使用指南\" - 查看')\n\n\/\/ 收藏标签\nlet likeTags = getFromCache(\"likeTags\")\nif (likeTags !== null && likeTags.length >= 1) {\n    likeTags.forEach(tag => {\n        let tagLink = {}\n        tagLink[tag] = `${urlSearchNovel(tag, \"{{page}}\")}`\n        likeTagLinks.push(tagLink)\n    })\n    li = li.concat(likeTagLinks)\n}\n\n\/\/ 他人收藏\nlet likeAuthors = getFromCacheMap(\"likeAuthors\")\nif (likeAuthors.size > 0) {\n    likeAuthors.forEach((authorName, authorId) => {\n        let bookmark = {}\n        bookmark[authorName] = urlUserBookmarks(authorId)\n        othersBookmarks.push(bookmark)\n    })\n    li = li.concat(othersBookmarks)\n}\n\n\/\/ 添加格式\nli.forEach(item => {\n    item.title = Object.keys(item)[0]\n    item.url = Object.values(item)[0]\n    if (item.url.includes(\"https:\/\/www.pixiv.net\")) item.url = urlIP(item.url)\n    delete item[Object.keys(item)[0]]\n    item.style = {}\n    item.style.layout_flexGrow = 1\n    item.style.layout_flexShrink = 1\n    item.style.layout_alignSelf = \"auto\"\n    item.style.layout_wrapBefore = \"false\"\n    if (item.url === \"\") {\n        item.style.layout_flexBasisPercent = 1\n    } else {\n        item.style.layout_flexBasisPercent = -1\n    }\n})\n\nJSON.stringify(li)",
    "header": "{\"referer\":\"https:\/\/www.pixiv.net\"}",
    "jsLib": "var checkTimes = 0\nvar cacheSaveSeconds = 7*24*60*60  \/\/ 长期缓存时间 7天\nvar cacheTempSeconds = 10*60*1000  \/\/ 短期缓存 10min\n\nfunction cacheGetAndSet(key, supplyFunc) {\n    const {java, cache} = this\n    let v = cache.get(key)\n    \/\/ 缓存信息错误时,保存 10min 后重新请求\n    if (v && JSON.parse(v).error === true) {\n        if (new Date().getTime() >= JSON.parse(v).timestamp + cacheTempSeconds) {\n            cache.delete(key)\n            v = cache.get(key)\n        }\n    }\n    \/\/ 无缓存信息时,进行请求\n    if (v === undefined || v === null) {\n        v = supplyFunc()\n        v.timestamp = new Date().getTime()\n        v = JSON.stringify(v)\n        cache.put(key, v, cacheSaveSeconds)\n    }\n    return JSON.parse(v)\n}\n\nfunction putInCache(objectName, object, saveSeconds) {\n    const {java, cache} = this\n    if (object === undefined) object = null\n    if (saveSeconds === undefined) saveSeconds = 0\n    cache.put(objectName, JSON.stringify(object), saveSeconds)\n}\nfunction getFromCache(objectName) {\n    const {java, cache} = this\n    let object = cache.get(objectName)\n    if (object === undefined) return null  \/\/ 兼容源阅\n    return JSON.parse(object)\n}\n\nfunction putInCacheMap(mapName, mapObject, saveSeconds) {\n    const {java, cache} = this\n    let orderedArray = []\n    mapObject.forEach((value, key) => {\n        const item = {}\n        item[key] = value\n        orderedArray.push(item)\n    })\n    \/\/ [{'key1': 'value1'}, {'key2': 'value2'}]\n    if (saveSeconds === undefined) saveSeconds = 0\n    cache.put(mapName, JSON.stringify(orderedArray), saveSeconds)\n}\nfunction getFromCacheMap(mapName) {\n    const {java, cache} = this\n    let cached = cache.get(mapName)\n    let newMap = new Map()\n    if (cached === null || cached === undefined) {\n        return newMap\n    }\n\n    let parsedData\n    try {\n        parsedData = JSON.parse(cached)\n    } catch (e) {\n        return newMap\n    }\n\n    if (Array.isArray(parsedData)) {\n        parsedData.forEach(item => {\n            for (let key in item) {\n                newMap.set(key, item[key])\n            }\n        })\n    } else {\n        for (let key in parsedData) {\n            newMap.set(key, parsedData[key])\n        }\n    }\n    return newMap\n}\n\nfunction isHtmlString(str) {\n    return str.startsWith(\"<!DOCTYPE html>\")\n}\nfunction isJsonString(str) {\n    try {\n        if (typeof JSON.parse(str) === \"object\") return true\n    } catch(e) {}\n    return false\n}\n\nfunction getWebViewUA() {\n    const {java, cache} = this\n    let userAgent = String(java.getWebViewUA())\n    if (userAgent.includes(\"Windows NT 10.0; Win64; x64\")) {\n        userAgent = \"Mozilla\/5.0 (Linux; Android 10; K) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/119.0.0.0 Mobile Safari\/537.36\"\n    }\n    \/\/ java.log(`userAgent=${userAgent}`)\n    cache.put(\"userAgent\", userAgent)\n    return String(userAgent)\n}\nfunction isLogin() {\n    const {java, cache} = this\n    return !!cache.get(\"pixivCsrfToken\")\n}\n\nfunction getAjaxJson(url, forceUpdate) {\n    const {java, cache} = this\n    let v = cache.get(url)\n    if (forceUpdate || v && new Date().getTime() >= JSON.parse(v).timestamp + cacheTempSeconds) {\n        cache.delete(url)\n    }\n    return this.cacheGetAndSet(url, () => {\n        return JSON.parse(java.ajax(url))\n    })\n}\nfunction getAjaxAllJson(urls, forceUpdate) {\n    const {java, cache} = this\n    let v = cache.get(urls)\n    if (forceUpdate || v && new Date().getTime() >= JSON.parse(v).timestamp + cacheTempSeconds) {\n        cache.delete(urls)\n    }\n    return this.cacheGetAndSet(urls, () => {\n        let result = java.ajaxAll(urls).map(resp => JSON.parse(resp.body()))\n        cache.put(urls, JSON.stringify(result), cacheSaveSeconds)\n        for (let i in urls) cache.put(urls[i], JSON.stringify(result[i]), cacheSaveSeconds)\n        return result\n    })\n}\nfunction getIPJson(url, forceUpdate) {\n    const {java, cache} = this\n    url = url.replace(\"http:\/\/\", \"https:\/\/\").replace(\"www.pixiv.net\", \"210.140.139.155\")\n    let v = cache.get(url)\n    if (forceUpdate || v && new Date().getTime() >= JSON.parse(v).timestamp + cacheTempSeconds) {\n        cache.delete(url)\n    }\n    let headers = {\n        \"User-Agent\": \"Mozilla\/5.0 (Linux; Android 14)\",\n        \"X-Requested-With\": \"XMLHttpRequest\",\n        \"Host\": \"www.pixiv.net\"\n    }\n    return this.cacheGetAndSet(url, () => {\n        return JSON.parse(java.get(url, headers).body())\n    })\n}\nfunction getWebviewJson(url, parseFunc) {\n    const {java, cache} = this\n    return this.cacheGetAndSet(url, () => {\n        let html = java.webView(null, url, null)\n        return JSON.parse(parseFunc(html))\n    })\n}\n\nfunction urlIP(url) {\n    const {java, cache, source} = this\n    let isIPDirect\n    if (source.bookSourceName.includes(\"备用\") || source.bookSourceName.includes(\"漫画\")) {\n        isIPDirect = JSON.parse(String(source.variableComment).match(RegExp(\/{([\\s\\S]*?)}\/gm)))?.IPDirect || false\n    } else {\n        isIPDirect = JSON.parse(cache.get(\"pixivSettings\")).IPDirect || false\n    }\n\n    if (isIPDirect) {\n        url = url.replace(\"http:\/\/\", \"https:\/\/\").replace(\"www.pixiv.net\", \"210.140.139.155\")\n        let headers = {\n            \"User-Agent\": \"Mozilla\/5.0 (Linux; Android 14)\",\n            \"X-Requested-With\": \"XMLHttpRequest\",\n            \"Host\": \"www.pixiv.net\",\n            \"x-csrf-token\": cache.get(\"pixivCsrfToken\") || \"\",\n            \"Cookie\": cache.get(\"pixivCookie\") || \"\"\n        }\n        return `${url}, ${JSON.stringify({headers: headers})}`\n    }\n    return url\n}\n\nfunction urlNovelUrl(novelId) {\n    return `https:\/\/www.pixiv.net\/novel\/show.php?id=${novelId}`\n}\nfunction urlNovelDetailed(novelId) {\n    return `https:\/\/www.pixiv.net\/ajax\/novel\/${novelId}`\n}\nfunction urlNovelsDetailed(userId, nidList) {\n    return `https:\/\/www.pixiv.net\/ajax\/user\/${userId}\/novels?${nidList.map(v => `ids[]=${v}`).join(\"&\")}`\n}\nfunction urlNovelBookmarkData(novelId) {\n    return `https:\/\/www.pixiv.net\/ajax\/novel\/${novelId}\/bookmarkData`\n}\nfunction urlNovelComments(novelId, offset, limit) {\n    return `https:\/\/www.pixiv.net\/ajax\/novels\/comments\/roots?novel_id=${novelId}&offset=${offset}&limit=${limit}&lang=zh`\n}\nfunction urlNovelCommentsReply(commentId, page) {\n    return `https:\/\/www.pixiv.net\/ajax\/novels\/comments\/replies?comment_id=${commentId}&page=${page}&lang=zh`\n}\nfunction urlNovelsRecommendInit(novelId, limit) {\n    if (limit === undefined) limit = 9\n    return `https:\/\/www.pixiv.net\/ajax\/novel\/${novelId}\/recommend\/init?limit=${limit}&lang=zh`\n}\nfunction urlNovelsRecommendDetailed(nidList) {\n    if (nidList.length >= 9) nidList.length = 9\n    return `https:\/\/www.pixiv.net\/ajax\/novel\/recommend\/novels?${nidList.map(v => `novelIds[]=${v}`).join(\"&\")}`\n}\n\nfunction urlSeriesUrl(seriesId) {\n    return `https:\/\/www.pixiv.net\/novel\/series\/${seriesId}`\n}\nfunction urlSeriesDetailed(seriesId) {\n    return `https:\/\/www.pixiv.net\/ajax\/novel\/series\/${seriesId}?lang=zh`\n}\nfunction urlSeriesNovelsTitles(seriesId) {\n    return `https:\/\/www.pixiv.net\/ajax\/novel\/series\/${seriesId}\/content_titles`\n}\nfunction urlSeriesNovels(seriesId, limit, offset) {\n    if (limit > 30) limit = 30\n    if (limit < 10) limit = 10\n    return `https:\/\/www.pixiv.net\/ajax\/novel\/series_content\/${seriesId}?limit=${limit}&last_order=${offset}&order_by=asc&lang=zh`\n}\n\nfunction urlUserUrl(userID) {\n    return `https:\/\/www.pixiv.net\/users\/${userID}\/novels`\n}\nfunction urlUserDetailed(userID) {\n    return `https:\/\/www.pixiv.net\/ajax\/user\/${userID}`\n}\nfunction urlUserWorkLatest(userID) {\n    return `https:\/\/www.pixiv.net\/ajax\/user\/${userID}\/works\/latest`\n}\nfunction urlUserAllWorks(userId) {\n    return `https:\/\/www.pixiv.net\/ajax\/user\/${userId}\/profile\/all?lang=zh`\n}\nfunction urlUserBookmarks(userId) {\n    return `https:\/\/www.pixiv.net\/ajax\/user\/${userId}\/novels\/bookmarks?tag=&offset={{(page-1)*30}}&limit=30&rest=show&lang=zh`\n}\n\nfunction urlSearchNovel(novelName, page) {\n    return `https:\/\/www.pixiv.net\/ajax\/search\/novels\/${encodeURI(novelName)}?word=${encodeURI(novelName)}&order=date_d&mode=all&p=${page}&s_mode=s_tag&lang=zh`\n}\nfunction urlSearchSeries(seriesName, page) {\n    return`https:\/\/www.pixiv.net\/ajax\/search\/novels\/${encodeURI(seriesName)}?word=${encodeURI(seriesName)}&order=date_d&mode=all&p=${page}&s_mode=s_tag&gs=1&lang=zh`\n}\n\/\/ 不完全匹配用户名\nfunction urlSearchUser(userName, full) {\n    if (full === undefined || full === false) {\n        return `https:\/\/www.pixiv.net\/search\/users?nick=${userName}&s_mode=s_usr&nick_mf=1`\n    } else {\n        return `https:\/\/www.pixiv.net\/search\/users?nick=${userName}&s_mode=s_usr_full&i=1`\n    }\n}\n\nfunction urlCoverUrl(url) {\n    const {java, cache, source} = this\n    let isIPDirect\n    if (source.bookSourceName.includes(\"备用\")|| source.bookSourceName.includes(\"漫画\")) {\n        isIPDirect = JSON.parse(String(source.variableComment).match(RegExp(\/{([\\s\\S]*?)}\/gm)))?.IPDirect || false\n    } else {\n        isIPDirect = JSON.parse(cache.get(\"pixivSettings\")).IPDirect || false\n    }\n\n    let headers = {\"Referer\": \"https:\/\/www.pixiv.net\/\"}\n    if (isIPDirect && url.trim()) {\n        if (url.includes(\"i.pximg.net\")) {\n            url = url.replace(\"https:\/\/i.pximg.net\", \"https:\/\/210.140.139.133\")\n            headers.host = \"i.pximg.net\"\n        } else {\n            url = url.replace(\"https:\/\/s.pximg.net\", \"https:\/\/210.140.139.133\")\n            headers.host = \"s.pximg.net\"\n        }\n    }\n    return `${url}, ${JSON.stringify({headers: headers})}`\n}\nfunction urlIllustDetailed(illustId) {\n    return `https:\/\/www.pixiv.net\/ajax\/illust\/${illustId}?lang=zh`\n}\nfunction urlIllustOriginal(illustId, order) {\n    const {java, cache} = this\n    if (order <= 1) order = 1\n    let url = this.urlIP(urlIllustDetailed(illustId))\n    let illustOriginal = this.cacheGetAndSet(url, () => {\n        return JSON.parse(java.ajax(url))\n    })?.body?.urls?.original || \"\"\n    return this.urlCoverUrl(illustOriginal.replace(`_p0`, `_p${order - 1}`))\n}\nfunction urlEmojiUrl(emojiId) {\n    return this.urlCoverUrl(`https:\/\/s.pximg.net\/common\/images\/emoji\/${emojiId}.png`)\n}\nfunction urlStampUrl(stampId) {\n    return this.urlCoverUrl(`https:\/\/s.pximg.net\/common\/images\/stamp\/generated-stamps\/${stampId}_s.jpg`)\n}\n\nfunction urlMessageThreadLatest(max) {\n    if (max === undefined || max <= 5) max = 5\n    return `https:\/\/www.pixiv.net\/rpc\/index.php?mode=latest_message_threads2&num=${max}&lang=zh`\n}\nfunction urlMessageThreadContents(threadId, max) {\n    return `https:\/\/www.pixiv.net\/rpc\/index.php?mode=message_thread_contents&thread_id=${threadId}&num=${max}`\n}\nfunction urlMessageThreadDetail(threadId) {\n    return `https:\/\/www.pixiv.net\/rpc\/index.php?mode=message_thread&thread_id=${threadId}`\n}\nfunction urlNotification() {\n    return `https:\/\/www.pixiv.net\/ajax\/notification&lang=zh`\n}\n\nfunction dateFormat(str) {\n    let addZero = function (num) {\n        return num < 10 ? '0' + num : num;\n    }\n    let time = new Date(str);\n    let Y = time.getFullYear() + \"年\";\n    let M = addZero(time.getMonth() + 1) + \"月\";\n    let D = addZero(time.getDate()) + \"日\";\n    return Y + M + D;\n}\nfunction timeFormat(str) {\n    let addZero = function (num) {\n        return num < 10 ? '0' + num : num;\n    }\n    let time = new Date(str);\n    let YY = time.getFullYear()\n    let MM = addZero(time.getMonth() + 1)\n    let DD = addZero(time.getDate())\n    let hh = addZero(time.getHours())\n    let mm = addZero(time.getMinutes())\n    let ss = addZero(time.getSeconds())\n    return `${YY}-${MM}-${DD} ${hh}:${mm}:${ss}`\n}\nfunction timeTextFormat(text) {\n    return `${text.slice(0, 10)} ${text.slice(11, 19)}`\n}\nfunction sleep(time) {\n    let endTime = new Date().getTime() + time\n    while(true){\n        if (new Date().getTime() > endTime){\n            return;\n        }\n    }\n}\nfunction sleepToast(text, second) {\n    const {java} = this\n    java.log(text)\n    \/\/ java.toast(text)\n    java.longToast(text)\n    if (second === undefined) second = 0.01\n    sleep(1000*second)\n}\n\nfunction updateSource() {\n    const {java, source} = this\n    java.longToast(\"🆙 更新书源\\n\\nJsdelivr CDN 更新有延迟\\nGithub 更新需代理\")\n    let onlineSource, comment, sourceName, sourceNameCapitalize, index = 0\n    if (source.bookSourceUrl.includes(\"pixiv\")) sourceName = \"pixiv\"\n    else if (source.bookSourceUrl.includes(\"furrynovel\")) sourceName = \"linpx\"\n    sourceNameCapitalize = sourceName[0].toUpperCase() + sourceName.substring(1)\n\n    if (source.bookSourceName.includes(\"备用\")) index = 1\n    else if (source.bookSourceName.includes(\"漫画\")) index = 2\n    if (source.bookSourceUrl.includes(\"furrynovel.com\")) {\n        sourceNameCapitalize = \"FurryNovel\"\n        index = 1\n    }\n\n    try {\n        let updateUrl = `https:\/\/cdn.jsdelivr.net\/gh\/DowneyRem\/PixivSource@main\/${sourceName}.json`\n        onlineSource = JSON.parse(java.get(updateUrl,{'User-Agent': 'Mozilla\/5.0 (Linux; Android 14)','X-Requested-With': 'XMLHttpRequest'}).body())[index]\n    } catch (e) {\n        try {\n            let updateUrl = `https:\/\/raw.githubusercontent.com\/DowneyRem\/PixivSource\/main\/${sourceName}.json`\n            onlineSource = JSON.parse(java.get(updateUrl,{'User-Agent': 'Mozilla\/5.0 (Linux; Android 14)','X-Requested-With': 'XMLHttpRequest'}).body())[index]\n        } catch (e) {\n            onlineSource = {lastUpdateTime: new Date().getTime(), bookSourceComment: source.bookSourceComment}\n        }\n    }\n    comment = onlineSource.bookSourceComment.split(\"\\n\")\n    \/\/ onlineSource = source\n    \/\/ comment = source.bookSourceComment.split(\"\\n\")\n\n    let htm = `\n<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>更新 ${source.bookSourceName} 书源<\/title>\n    <style> \n    table { text-align: center; margin: 0 auto; } .ann { display: flex; justify-content: center; align-items: center; height: 5vh; } \n    button { background-color: rgb(76, 175, 80); color: white; border: none; border-radius: 4px; height: 6vh; width: 30vw; overflow: hidden; } \n    button span { cursor: pointer; display: inline-block; position: relative; transition: 0.4s; } \n    button span:after { content: '>'; position: absolute; opacity: 0; top: 0; right: 30px; transition: 0.2s; } \n    button:active span { padding-right: 20px; } \n    button:active span:after { opacity: 1; right: -40px; }\n    <\/style>\n<\/head>\n\n<body>\n    <table border=\"1\" cellspacing=\"0\">\n        <th colspan=\"2\"> ${source.bookSourceName} 书源 \n        <a href=\"https:\/\/github.com\/DowneyRem\/PixivSource\/blob\/main\/doc\/${sourceNameCapitalize}.md\">🔰 使用指南<\/a>\n        ||<a href=\"https:\/\/github.com\/DowneyRem\/PixivSource\/blob\/main\/doc\/Sponsor.md\">❤️ 赞助开发<\/a>\n        <\/th>\n        <tr>\n            <td>☁️ 远程版本:${onlineSource.bookSourceComment.split(\"\\n\")[2].replace(\"书源版本:\", \"\")}<\/td>\n            <td>📆 更新:${timeFormat(onlineSource.lastUpdateTime)}<\/td>\n        <\/tr>\n        <tr>\n            <td>📥 本地版本:${source.bookSourceComment.split(\"\\n\")[2].replace(\"书源版本:\", \"\")}<\/td>\n            <td>📆 更新:${timeFormat(source.lastUpdateTime)}<\/td>\n        <\/tr> \n        <tr><td colspan=\"2\" style=\"text-align: left;\">${comment.slice(3, 10).join(\"<br>\")}<\/td><\/tr>\n        <tr><td colspan=\"2\" style=\"text-align: left;\">${comment.slice(comment.length-20, comment.length).join(\"<br>\")}<\/td><\/tr>\n    <\/table>\n    \n    <table border=\"0\" cellspacing=\"20\">\n        <th colspan=\"2\"> 更新 ${source.bookSourceName} 书源 <\/th>\n        <tr><td><div class=\"ann\">\n            <a href=\"legado:\/\/import\/importonline?src=https:\/\/cdn.jsdelivr.net\/gh\/DowneyRem\/PixivSource@main\/${sourceName}.json\">\n            <button><span>更新书源<br>(Jsdelivr CDN)<\/span><\/button>\n            <\/a><\/div><\/td>\n            \n            <td><div class=\"ann\">\n            <a href=\"legado:\/\/import\/importonline?src=https:\/\/cdn.jsdelivr.net\/gh\/DowneyRem\/PixivSource@main\/btsrk.json\">\n            <button><span>更新订阅<br>(Jsdelivr CDN)<\/span><\/button>\n            <\/a><\/div><\/td>\n        <\/tr>\n        \n        <tr><td><div class=\"ann\">\n            <a href=\"legado:\/\/import\/importonline?src=https:\/\/raw.githubusercontent.com\/DowneyRem\/PixivSource\/main\/${sourceName}.json\">\n            <button><span>书源链接<br>(GitHub)<\/span><\/button>\n            <\/a><\/div><\/td>\n            \n            <td><div class=\"ann\">\n            <a href=\"legado:\/\/import\/importonline?src=https:\/\/raw.githubusercontent.com\/DowneyRem\/PixivSource\/main\/btsrk.json\">\n            <button><span>订阅链接<br>(GitHub)<\/span><\/button>\n            <\/a><\/div><\/td>\n        <\/tr>\n        \n        <tr><td><div class=\"ann\">\n            <a href=\"legado:\/\/import\/importonline?src=https:\/\/codeberg.org\/DowneyRem\/PixivSource\/raw\/branch\/main\/${sourceName}.json\">\n            <button><span>备用书源链接<br>(Codeberg)<\/span><\/button>\n            <\/a><\/div><\/td>\n            \n            <td><div class=\"ann\">\n            <a href=\"legado:\/\/import\/importonline?src=https:\/\/codeberg.org\/DowneyRem\/PixivSource\/raw\/branch\/main\/btsrk.json\">\n            <button><span>备用订阅链接<br>(Codeberg)<\/span><\/button>\n            <\/a><\/div><\/td>\n        <\/tr>\n    <\/table>\n<\/body>\n<\/html>`\n    java.startBrowser(`data:text\/html;charset=utf-8;base64, ${java.base64Encode(htm)}`, '更新书源')\n    return []\n}",
    "lastUpdateTime": "1763624155218",
    "loginCheckJs": "var util = {}\n\nfunction objStringify(obj) {\n    return JSON.stringify(obj, (n, v) => {\n        if (typeof v == \"function\")\n            return v.toString();\n        return v;\n    });\n}\nfunction isBackupSource() {\n    let isBackupSource = source.bookSourceName.includes(\"备用\")\n    cache.put(\"isBackupSource\", isBackupSource)\n    return isBackupSource\n}\n\/\/ 检测 源阅\n\/\/ 可用 java.ajax() 不可用 java.webview() java.ajaxAll()\n\/\/ 可用 java.getCookie() cache.put() cache.get() 默认值为 undefined\n\/\/ 可用 java.startBrowser() 不可用 java.startBrowserAwaitAwait\n\/\/ 可用 source.bookSourceName source.getVariable() source.setVariable()等\n\/\/ java.getUserAgent() java.getWebViewUA() 目前返回内容相同\nfunction isSourceRead() {\n    let isSourceReadStatus = java.getUserAgent() === java.getWebViewUA()\n    cache.put(\"isSourceRead\", isSourceReadStatus)\n    return isSourceReadStatus\n}\n\/\/ 检测 阅读 正式版 与 Beta 版本\nfunction isLegadoOfficial() {\n    let isLegadoOfficialStatus\n    try {\n        eval('({})?.value')\n        isLegadoOfficialStatus = false\n    } catch (e) {\n        isLegadoOfficialStatus = true\n    }\n    cache.put(\"isLegadoOfficial\", isLegadoOfficialStatus)\n    return isLegadoOfficialStatus\n}\n\/\/ 检测 阅读 Beta 版本 与 LYC 版本\n\/\/ LYC 版本新增函数\n\/\/ java.ajaxTestAll()\n\/\/ java.openVideoPlayer(url: String, title: String, float: Boolean)\n\/\/ cookie.setWebCookie(url,cookie)\n\/\/ source.refreshExplore()\n\/\/ source.refreshJSLib()\nfunction isLegadoLYC() {\n    let isLegadoLYCStatus = (typeof java.ajaxTestAll === \"function\")\n    cache.put(\"isLegadoLYCStatus\", isLegadoLYCStatus)\n    return isLegadoLYCStatus\n}\n\nfunction publicFunc() {\n    let u = {}, settings\n    \/\/ 输出书源信息\n    java.log(`🅿️ ${source.bookSourceComment.split(\"\\n\")[0]}`)\n    java.log(`📌 ${source.bookSourceComment.split(\"\\n\")[2]}`)\n    java.log(`📆 更新时间:${java.timeFormat(source.lastUpdateTime)}`)\n    if (isSourceRead()) {\n        java.log(\"📱 软件平台:🍎 源阅 SourceRead\")\n    } else if (isLegadoOfficial()) {\n        java.log(\"📱 软件平台:🤖 开源阅读 【正式版】\")\n        java.log(\"当前软件为:阅读【正式版】\\n\\n【正式版】已年久失修,不推荐继续使用\\n推荐使用【Beta版】【共存\/新共存版】\\n\\nBeta版本下载链接:\\nhttps:\/\/miaogongzi.lanzout.com\/b01rgkhhe\\n如需更新,可去书源调试界面\\n打开下载链接切换阅读版本\\n\")\n    } else {\n        if (isLegadoLYC()) {\n            java.log(\"📱 软件平台:🤖 开源阅读 Beta\/LYC 版\")\n        } else {\n            java.log(\"📱 软件平台:🤖 开源阅读 Beta 版(未合入 LYC 功能)\")\n        }\n    }\n\n    \/\/ 获取设置,备用书源使用旧版设置,书源从缓存获取设置\n    if (isBackupSource()) {\n        settings = JSON.parse(String(source.variableComment).match(RegExp(\/{([\\s\\S]*?)}\/gm)))\n    } else {\n        \/\/ cache.delete(\"pixivSettings\")\n        settings = getFromCache(\"pixivSettings\")\n    }\n    let isIPDirect = settings?.IPDirect || false\n    if (isIPDirect) java.log(\"✈️ 直连模式:✅ 已开启\")\n\n    \/\/ 初始化设置\n    if (settings !== null) {\n        java.log(\"⚙️ 使用自定义设置\")\n    } else {\n        settings = {}\n        settings.SEARCH_AUTHOR = true       \/\/ 搜索:默认搜索作者名称\n        settings.CONVERT_CHINESE = true     \/\/ 搜索:搜索时进行繁简转换\n        settings.SHOW_LIKE_NOVELS = true    \/\/ 搜索:搜索结果显示收藏小说\n        settings.SHOW_WATCHED_SERIES = true \/\/ 搜索:搜索结果显示追整系列小说\n\n        settings.MORE_INFORMATION = false   \/\/ 详情:书籍简介显示更多信息\n        settings.SHOW_UPDATE_TIME = true    \/\/ 目录:显示更新时间,但会增加少许请求\n        settings.SHOW_ORIGINAL_LINK = true  \/\/ 目录:显示原始链接,但会增加大量请求\n\n        settings.REPLACE_TITLE_MARKS = true \/\/ 正文:注音内容为汉字时,替换为书名号\n        settings.SHOW_CAPTIONS = true       \/\/ 正文:章首显示描述\n        settings.SHOW_COMMENTS = true       \/\/ 正文:章尾显示评论\n\n        settings.IPDirect = false           \/\/ 全局:直连模式\n        settings.FAST  = false              \/\/ 全局:快速模式\n        settings.DEBUG = false              \/\/ 全局:调试模式\n        java.log(\"⚙️ 使用默认设置(无自定义设置 或 自定义设置有误)\")\n    }\n\n    if (settings.FAST) {\n        settings.SEARCH_AUTHOR = false        \/\/ 搜索:默认搜索作者名称\n        settings.CONVERT_CHINESE = false      \/\/ 搜索:繁简通搜\n        settings.SHOW_UPDATE_TIME = false     \/\/ 目录:显示章节更新时间\n        settings.SHOW_ORIGINAL_LINK = false   \/\/ 目录:显示章节源链接\n        settings.SHOW_COMMENTS = false        \/\/ 正文:显示评论\n    } else {\n        settings.FAST = false\n        settings.SEARCH_AUTHOR = true         \/\/ 搜索:默认搜索作者名称\n    }\n\n    if (settings.IPDirect) {\n        settings.SEARCH_AUTHOR = false       \/\/ 搜索:默认关闭搜索作者名称\n        settings.SHOW_ORIGINAL_LINK = false  \/\/ 目录:不显示章节源链接\n    } else {\n        settings.IPDirect = false\n        settings.SEARCH_AUTHOR = true        \/\/ 搜索:默认关闭搜索作者名称\n        settings.SHOW_ORIGINAL_LINK = true   \/\/ 目录:不显示章节源链接\n    }\n\n    u.settings = settings\n    putInCache(\"pixivSettings\", settings)  \/\/ 设置写入缓存\n\n    u.environment = {}\n    u.environment.IS_SOURCEREAD = isSourceRead()\n    u.environment.IS_LEGADO = !isSourceRead()\n    u.environment.IS_LYC_BRUNCH = isLegadoLYC()\n    u.environment.IS_BACKUP_SOURCE = isBackupSource()\n    putInCache(\"sourceEnvironment\", u.environment)  \/\/ 设置写入缓存\n\n    u.debugFunc = (func) => {\n        if (util.settings.DEBUG === true) {\n            func()\n        }\n    }\n\n    u.checkStatus = function(status) {\n        if (status === true) return \"✅ 已\"\n        else if (status === false) return \"❌ 未\"\n        else if (status === undefined) return \"🈚️ 无数据:\"\n    }\n\n    u.login = function() {\n        let resp = java.startBrowserAwait(`https:\/\/accounts.pixiv.net\/login,\n    {\"headers\": {\"User-Agent\": \"${java.getWebViewUA()}\"}}`, '登录账号', false)\n        if (resp.code() === 200) {\n            this.getCsrfToken(); this.getCookie()\n        } else {\n            java.log(resp.code()); sleepToast(\"⚠️ 登录失败\")\n        }\n    }\n\n    u.logout = function() {\n        this.removeCookie()\n        java.startBrowser(\"https:\/\/www.pixiv.net\/logout.php\", \"退出账号\")\n        this.removeCookie()\n        sleepToast(`✅ 已退出当前账号\\n\\n退出后请点击右上角的 ✔️ 退出\\n\\n登录请点击【登录账号】进行登录`)\n    }\n\n    u.getCookie = function() {\n        let pixivCookie = String(java.getCookie(\"https:\/\/www.pixiv.net\/\", null))\n        if (isLogin()) cache.put(\"pixivCookie\", pixivCookie, 60*60)  \/\/ 缓存1h\n    }\n\n    u.removeCookie = function() {\n        cookie.removeCookie('https:\/\/www.pixiv.net')\n        cookie.removeCookie('https:\/\/accounts.pixiv.net')\n        cookie.removeCookie('https:\/\/accounts.google.com')\n        cookie.removeCookie('https:\/\/api.weibo.com')\n        cache.delete(\"pixivCookie\")\n        cache.delete(\"pixiv:uid\")\n        cache.delete(\"pixivCsrfToken\")  \/\/ 与登录设备有关\n        cache.delete(\"headers\")\n    }\n\n    \/\/ 获取 Csrf Token,以便进行收藏等请求\n    \/\/ 获取方法来自脚本 Pixiv Previewer\n    \/\/ https:\/\/github.com\/Ocrosoft\/PixivPreviewer\n    \/\/ https:\/\/greasyfork.org\/zh-CN\/scripts\/30766-pixiv-previewer\/code\n    u.getCsrfToken = function() {\n        let pixivCsrfToken = cache.get(\"pixivCsrfToken\")\n        if (!pixivCsrfToken) {\n            let html = java.webView(null, \"https:\/\/www.pixiv.net\/\", null)\n            try {\n                pixivCsrfToken = html.match(\/token\\\\\":\\\\\"([a-z0-9]{32})\/)[1]\n                cache.put(\"pixivCsrfToken\", pixivCsrfToken)  \/\/ 与登录设备有关,无法存储 nul\n            } catch (e) {\n                pixivCsrfToken = null\n                cache.delete(\"pixivCsrfToken\")  \/\/ 与登录设备有关,无法存储 nul\n                \/\/ sleepToast(\"⚠️ 未登录账号(pixivCsrfToken)\")\n            }\n            java.log(`pixivCsrfToken:\\n${pixivCsrfToken}`)\n        }\n        return pixivCsrfToken\n    }\n\n    \/\/ 将多个长篇小说解析为一本书\n    u.combineNovels = function(novels) {\n        return novels.filter(novel => {\n            \/\/ 单本直接解析为一本书\n            if (novel.seriesId === undefined || novel.seriesId === null) {\n                return true\n            }\n            \/\/ 集合中没有该系列解析为一本书\n            if (!seriesSet.has(novel.seriesId)) {\n                seriesSet.add(novel.seriesId)\n                return true\n            }\n            return false\n        })\n    }\n\n    \/\/ 屏蔽作者\n    u.authorFilter = function(novels) {\n        let authors = getFromCache(\"blockAuthorList\")\n        if (authors !== null && authors.length >= 0) {\n            java.log(`🚫 屏蔽作者ID:${JSON.stringify(authors)}`)\n            authors.forEach(author => {\n                novels = novels.filter(novel => novel.userId !== String(author))\n            })\n        }\n        return novels\n    }\n\n\n    \/\/ 过滤收藏与追更\n    u.novelFilter = function(novels) {\n        let novels1 = [], novels2 = [], msg\n        let likeNovels = getFromCache(\"likeNovels\")\n        let watchedSeries = getFromCache(\"watchedSeries\")\n        let novels0 = novels.map(novel => novel.id)\n\n        msg = util.checkStatus(util.settings.SHOW_LIKE_NOVELS).replace(\"未\",\"不\")\n        java.log(`${msg}显示收藏小说`)\n        if (util.settings.SHOW_LIKE_NOVELS === false) {\n            novels = novels.filter(novel => !likeNovels.includes(Number(novel.id)))\n            novels1 = novels.map(novel => novel.id)\n            java.log(`⏬ 过滤收藏:过滤前${novels0.length};过滤后${novels1.length}`)\n        }\n\n        msg = util.checkStatus(util.settings.SHOW_WATCHED_SERIES).replace(\"未\",\"不\")\n        java.log(`${msg}显示追更系列`)\n        if (util.settings.SHOW_WATCHED_SERIES === false) {\n            novels = novels.filter(novel => !watchedSeries.includes(Number(novel.seriesId)))\n            novels2 = novels.map(novel => novel.id)\n            if (novels1.length >= 1) novels0 = novels1\n            java.log(`⏬ 过滤追更:过滤前${novels0.length};过滤后${novels2.length}`)\n        }\n\n        let novels3 = novels.map(novel => novel.id)\n        if (novels0.length >= 1 && novels3.length === 0) {\n            let msg = `⏬ 过滤小说\\n⚠️ 过滤后无结果\\n\\n请根据需要\\n`\n            if (util.settings.SHOW_LIKE_NOVELS === false) msg += \"开启显示收藏小说\\n\"\n            if (util.settings.SHOW_WATCHED_SERIES === false) msg += \"开启显示追更系列\"\n            sleepToast(msg, 1)\n        }\n\n        util.debugFunc(() => {\n            \/\/ java.log(JSON.stringify(novels0))\n            java.log(JSON.stringify(novels0.length))\n            \/\/ java.log(JSON.stringify(novels1))\n            java.log(JSON.stringify(novels1.length))\n            \/\/ java.log(JSON.stringify(novels2))\n            java.log(JSON.stringify(novels2.length))\n        })\n        return novels\n    }\n\n    \/\/ 过滤描述与标签(屏蔽标签\/屏蔽描述)\n    u.novelFilter2 = function(novels) {\n        let novels0 = novels.map(novel => novel.id)\n        let captionBlockWords = getFromCache(\"captionBlockWords\")\n        if (captionBlockWords === null) captionBlockWords = []\n        if (captionBlockWords) {\n            \/\/ 仅保留没有任何屏蔽词的小说\n            \/\/ novels = novels.filter(novel => {\n            \/\/     return !captionBlockWords.some(item => {\n            \/\/         if (novel.description !== undefined) return novel.description.includes(item)\n            \/\/     })\n            \/\/ })\n            novels = novels.filter(novel => !captionBlockWords.some(item => novel.description.includes(item)))\n            let novels2 = novels.map(novel => novel.id)\n            java.log(`🚫 屏蔽描述:${captionBlockWords.join(\"\\n\")}`)\n            java.log(`🚫 屏蔽描述:过滤前${novels0.length};过滤后${novels2.length}`)\n        }\n\n        let tagsBlockWords = getFromCache(\"tagsBlockWords\")\n        if (tagsBlockWords === null) tagsBlockWords = []\n        if (tagsBlockWords) {\n            \/\/ 仅保留没有任何屏蔽词的小说\n            \/\/ novels = novels.filter(novel => {\n            \/\/     return !tagsBlockWords.some(item => {\n            \/\/         if (novel.tags !== undefined) return novel.tags.includes(item)\n            \/\/     })\n            \/\/ })\n            novels = novels.filter(novel => !tagsBlockWords.some(item => novel.tags.includes(item)))\n            let novels2 = novels.map(novel => novel.id)\n            java.log(`🚫 屏蔽标签:${tagsBlockWords.join(\"、\")}`)\n            java.log(`🚫 屏蔽标签:过滤前${novels0.length};过滤后${novels2.length}`)\n        }\n        return novels\n    }\n\n    \/\/ 收藏小说\/追更系列 写入缓存\n    u.saveNovels = function(listInCacheName, list) {\n        let listInCache = getFromCache(listInCacheName)\n        if (listInCache === null) listInCache = []\n\n        listInCache = listInCache.concat(list)\n        listInCache = Array.from(new Set(listInCache))\n        cache.put(listInCacheName, JSON.stringify(listInCache))\n\n        if (listInCacheName === \"likeNovels\") listInCacheName = \"❤️ 收藏小说ID\"\n        else if (listInCacheName === \"watchedSeries\") listInCacheName = \"📃 追更系列ID\"\n        java.log(`${listInCacheName}:${JSON.stringify(listInCache)}`)\n    }\n\n    \/\/ 处理 novels 列表\n    u.handNovels = function(novels) {\n        let likeNovels = [], watchedSeries = []\n        novels = util.authorFilter(novels)\n        novels.forEach(novel => {\n            \/\/ novel.id = novel.id\n            \/\/ novel.title = novel.title\n            \/\/ novel.userName = novel.userName\n            \/\/ novel.userId = novel.userId\n            \/\/ novel.tags = novel.tags\n            cache.put(`${novel.userName}`, novel.userId)  \/\/ 加入缓存,便于搜索作者\n            if (novel.tags === undefined || novel.tags === null) {\n                novel.tags = []\n            }\n            \/\/ 搜索单篇\n            if (novel.isOneshot === undefined) {\n                \/\/ novel.seriesId = novel.seriesId\n                \/\/ novel.seriesTitle = novel.seriesTitle\n                \/\/ novel.textCount = novel.textCount\n                \/\/ novel.description = novel.description\n                novel.coverUrl = novel.url\n                \/\/ novel.createDate = novel.createDate\n                \/\/ novel.updateDate = novel.updateDate\n            }\n\n            \/\/ 搜索系列\n            if (novel.isOneshot !== undefined) {\n                if (novel.isOneshot === true) {\n                    novel.seriesId = undefined\n                    novel.id = novel.novelId  \/\/ 获取真正的 novelId\n                    novel.seriesTitle = undefined\n                } else {\n                    novel.seriesId = novel.id\n                    novel.id = novel.novelId = novel.latestEpisodeId  \/\/ 获取真正的 novelId\n                    novel.seriesTitle = novel.title\n                    \/\/ novel.isWatched = novel.isWatched  \/\/ 搜索系列可获取\n                }\n                novel.textCount = novel.textLength\n                novel.description = novel.caption\n                novel.coverUrl = novel.cover.urls[\"480mw\"]\n                novel.createDate = novel.createDateTime\n                novel.updateDate = novel.updateDateTime\n            }\n\n            \/\/ 单篇正文详情页\n            if (novel.content) {\n                novel.novelId = novel.id\n                novel.tags = novel.tags.tags.map(item => item.tag)\n                novel.textCount = novel.userNovels[`${novel.id}`].textCount\n                \/\/ novel.latestChapter = novel.title\n                \/\/ novel.description = novel.description\n                novel.coverUrl = novel.userNovels[`${novel.id}`].url\n                \/\/ novel.createDate = novel.createDate\n                novel.updateDate = novel.uploadDate\n\n                if (novel.seriesNavData) {\n                    novel.seriesId = novel.seriesNavData.seriesId\n                    novel.seriesTitle = novel.seriesNavData.title\n                }\n            }\n\n            \/\/ 系列详情\n            if (novel.firstNovelId) {\n                novel.seriesId = novel.id\n                novel.id = novel.novelId = novel.firstNovelId\n                novel.seriesTitle = novel.title\n                novel.coverUrl = novel.cover.urls[\"480mw\"]\n                \/\/ novel.isWatched = novel.isWatched  \/\/ 搜索系列可获取\n            }\n\n            \/\/ 单篇加更多信息\n            if (!novel.seriesId) {\n                novel.tags.unshift(\"单本\")\n                novel.latestChapter = novel.title\n                novel.detailedUrl = urlIP(urlNovelDetailed(novel.id))\n                novel.total = 1\n                if (novel.bookmarkData) {\n                    novel.isBookmark = true\n                    cache.put(`collect${novel.id}`, novel.bookmarkData.id)\n                    likeNovels.push(Number(novel.id))\n                } else {\n                    novel.isBookmark = false\n                }\n            }\n            \/\/ 系列添加更多信息\n            if (novel.seriesId) {\n                let series = getAjaxJson(urlIP(urlSeriesDetailed(novel.seriesId))).body\n                novel.id = series.firstNovelId\n                novel.title = series.title\n                novel.tags = novel.tags.concat(series.tags)\n                novel.tags.unshift(\"长篇\")\n                novel.textCount = series.publishedTotalCharacterCount\n                novel.description = series.caption\n                novel.coverUrl = series.cover.urls[\"480mw\"]\n                novel.detailedUrl = urlIP(urlSeriesDetailed(novel.seriesId))\n                novel.createDate = series.createDate\n                novel.updateDate = series.updateDate\n                novel.total = series.publishedContentCount\n                novel.isWatched = series.isWatched\n                if (novel.isWatched === true) {\n                    watchedSeries.push(Number(novel.seriesId))\n                }\n\n                \/\/ 防止系列首篇无权限获取\n                \/\/ 发送请求获取第一章 获取标签与简介\n                let firstNovel = {}\n                try {\n                    firstNovel = getAjaxJson(urlIP(urlSeriesNovels(novel.seriesId, 30, 0))).body.thumbnails.novel[0]\n                    novel.id = novel.firstNovelId = firstNovel.id\n                    novel.tags = novel.tags.concat(firstNovel.tags)\n                } catch (e) { \/\/ 防止系列首篇无权限获取\n                    firstNovel = {}\n                    firstNovel.description = \"\"\n                }\n                novel.tags.unshift(\"长篇\")\n                if (novel.description === \"\") {\n                    novel.description = firstNovel.description\n                }\n            }\n        })\n        \/\/ 收藏小说\/追更系列 写入缓存\n        util.saveNovels(\"likeNovels\", likeNovels)\n        util.saveNovels(\"watchedSeries\", watchedSeries)\n        util.debugFunc(() => {\n            java.log(`处理小说完成`)\n        })\n        return novels\n    }\n\n    \/\/ 小说信息格式化\n    u.formatNovels = function(novels) {\n        novels = util.novelFilter(novels)\n        novels.forEach(novel => {\n            if (novel.title) novel.title = novel.title.trim()\n            if (!novel.userName.startsWith(\"@\")) novel.userName = `@${novel.userName}`\n            novel.coverUrl = urlCoverUrl(novel.coverUrl)\n            novel.readingTime = `${novel.readingTime \/ 60} 分钟`\n            novel.createDate = dateFormat(novel.createDate)\n            novel.updateDate = dateFormat(novel.updateDate)\n\n            novel.tags2 = []\n            for (let i in novel.tags) {\n                let tag = novel.tags[i]\n                if (tag.includes(\"\/\")) {\n                    let tags = tag.split(\"\/\")\n                    novel.tags2 = novel.tags2.concat(tags)\n                } else {\n                    novel.tags2.push(tag)\n                }\n            }\n            novel.tags = Array.from(new Set(novel.tags2))\n            novel.tags = novel.tags.join(\",\")\n            if (novel.seriesId) {\n                collectMsg = `📃 追更:${util.checkStatus(novel.isWatched)}追更系列`\n            } else {\n                collectMsg = `❤️ 收藏:${util.checkStatus(novel.isBookmark)}加入收藏`\n            }\n\n            if (util.settings.MORE_INFORMATION) {\n                novel.description = `\\n🅿️ 登录:${util.checkStatus(isLogin())}登录账号\n                ${collectMsg}\\n📖 书名:${novel.title}\\n👤 作者:${novel.userName}\n                #️ 标签:${novel.tags}\\n⬆️ 上传:${novel.createDate}\n                🔄 更新:${novel.updateDate}\\n📄 简介:${novel.description}`\n            } else {\n                novel.description = `\\n🅿️ 登录:${util.checkStatus(isLogin())}登录账号\n                ${collectMsg}\\n⬆️ 上传:${novel.createDate}\\n🔄 更新:${novel.updateDate}\n                📄 简介:${novel.description}`\n            }\n        })\n        novels = util.novelFilter2(novels)\n        return novels\n    }\n\n    \/\/ 正文,详情,搜索:从网址获取id,返回单篇小说 res,系列返回首篇小说 res\n    \/\/ pixiv 默认分享信息中有#号,不会被识别成链接,无法使用添加网址\n    u.getNovelResFirst = function(result) {\n        let novelId = 0, res = {\"body\": {}}\n        let isJson = isJsonString(result)\n        let isHtml = isHtmlString(result)\n\n        if (!isJson && isHtml) {\n            let id = baseUrl.match(new RegExp(\"\\\\d+\"))[0]\n            let pattern = \"(https?:\/\/)?(www\\\\.)?pixiv\\\\.net(\/ajax)?\/users?\/\\\\d+\"\n            let isAuthor = baseUrl.match(new RegExp(pattern))\n            if (isAuthor) {\n                java.log(`作者ID:${id}`)\n                novelId = Object.keys(getAjaxJson(urlIP(urlUserWorkLatest(id))).body.novels).reverse()[0]\n            }\n\n            pattern = \"(https?:\/\/)?(www\\\\.)?pixiv\\\\.net\/novel\/series\/\\\\d+\"\n            let isSeries = baseUrl.match(new RegExp(pattern))\n            if (isSeries) {\n                java.log(`系列ID:${id}`)\n                novelId = getAjaxJson(urlIP(urlSeriesNovels(id, 30, 0))).body.thumbnails.novel[0].id\n            } else {\n                let pattern = \"(https?:\/\/)?(www\\\\.)?pixiv\\\\.net\/novel\/(show\\\\.php\\\\?id=)?\\\\d+\"\n                let isNovel = baseUrl.match(new RegExp(pattern))\n                if (isNovel) {\n                    novelId = id\n                }\n            }\n        }\n        if (isJson) {\n            res = JSON.parse(result)\n        }\n\n        if (novelId) {\n            java.log(`匹配小说ID:${novelId}`)\n            res = getAjaxJson(urlIP(urlNovelDetailed(novelId)))\n        }\n        if (res.error === true) {\n            java.log(`无法从 Pixiv 获取当前小说`)\n            java.log(JSON.stringify(res))\n        }\n        return res.body\n    }\n\n    \/\/ 目录:从网址获取id,尽可能返回系列 res,单篇小说返回小说 res\n    u.getNovelResSeries = function(result) {\n        let seriesId = 0, res = {\"body\": {}}\n        let isJson = isJsonString(result)\n        let isHtml = isHtmlString(result)\n\n        if (!isJson && isHtml) {\n            let id = baseUrl.match(new RegExp(\"\\\\d+\"))[0]\n            let pattern = \"(https?:\/\/)?(www\\\\.)?pixiv\\\\.net\/novel\/series\/\\\\d+\"\n            let isSeries = baseUrl.match(new RegExp(pattern))\n            if (isSeries) {\n                seriesId = id\n            } else {\n                let pattern = \"(https?:\/\/)?(www\\\\.)?pixiv\\\\.net\/novel\/(show\\\\.php\\\\?id=)?\\\\d+\"\n                let isNovel = baseUrl.match(new RegExp(pattern))\n                if (isNovel) {\n                    java.log(`匹配小说ID:${id}`)\n                    res = getAjaxJson(urlIP(urlNovelDetailed(id)))\n                }\n            }\n        }\n        if (isJson) {\n            res = JSON.parse(result)\n        }\n\n        if (res.body && res.body.seriesNavData) {\n            seriesId = res.body.seriesNavData.seriesId\n        }\n        if (seriesId) {\n            java.log(`系列ID:${seriesId}`)\n            res = getAjaxJson(urlIP(urlSeriesDetailed(seriesId)))\n        }\n        if (res.error === true) {\n            java.log(`无法从 Pixiv 获取当前小说`)\n            java.log(JSON.stringify(res))\n        }\n        return res.body\n    }\n\n    util = u\n    java.put(\"util\", objStringify(u))\n}\n\nfunction checkMessageThread(checkTimes) {\n    if (checkTimes === undefined) {\n        checkTimes = Number(cache.get(\"checkTimes\"))\n    }\n    if (checkTimes === 0 && isLogin()) {\n        let latestMsg = getAjaxJson(urlIP(urlMessageThreadLatest(5)))\n        if (latestMsg.error === true) {\n            java.log(JSON.stringify(latestMsg))\n        } else if (latestMsg.body.total >= 1) {\n            let msg = latestMsg.body.message_threads.filter(item => item.thread_name === \"pixiv事務局\")[0]\n            if (msg && new Date().getTime()- 1000*msg.modified_at <= 3*24*60*60*1000) { \/\/ 3天内进行提示\n                sleepToast(`您于 ${timeFormat(1000*msg.modified_at)} 触发 Pixiv 【过度访问】,请修改密码并重新登录。\\n如已修改请忽略`, 3)\n                sleepToast(`${msg.latest_content}`, 5)\n                java.startBrowser(\"https:\/\/accounts.pixiv.net\/password\/change\",'修改密码')\n            }\n        }\n    }\n    cache.put(\"checkTimes\", checkTimes + 1, 4*60*60)  \/\/ 缓存4h,每4h提醒一次\n    \/\/ cache.put(\"checkTimes\", checkTimes + 1, 60)  \/\/ 测试用,缓存60s,每分钟提醒一次\n    \/\/ java.log(checkTimes + 1)\n}\n\n\/\/ 获取请求的user id方便其他ajax请求构造\nfunction getPixivUid() {\n    let uid = cache.get(\"pixiv:uid\")\n    if (!uid || String(uid) === \"null\") {\n        let html = java.webView(null, \"https:\/\/www.pixiv.net\/\", null)\n        try {\n            uid = html.match(\/user_id:'(\\d+)'\/)[1]\n        } catch (e) {\n            uid = null\n        }\n        cache.put(\"pixiv:uid\", String(uid))\n    }\n}\n\nfunction getHeaders() {\n    let headers = {\n        \"accept\": \"application\/json\",\n        \"accept-encoding\": \"gzip, deflate, br, zstd\",\n        \"accept-language\": \"zh-CN\",\n        \/\/ \"content-type\": \"application\/json; charset=utf-8\",\n        \/\/ \"content-type\": \"application\/x-www-form-urlencoded; charset=utf-8\",\n        \"origin\": \"https\/\/www.pixiv.net\",\n        \"referer\": \"https:\/\/www.pixiv.net\/\",\n        \/\/ \"sec-ch-ua\": `\"Not\/A)Brand\";v=\"8\", \"Chromium\";v=\"132\", \"Google Chrome\";v=\"132\"`,\n        \/\/ \"sec-ch-ua-mobile\": \"?0\",\n        \/\/ \"sec-ch-ua-platform\": \"Windows\",\n        \/\/ \"sec-fetch-dest\": \"empty\",\n        \/\/ \"sec-fetch-mode\": \"cors\",\n        \/\/ \"sec-fetch-site\": \"same-origin\",\n        \"user-agent\": cache.get(\"userAgent\"),\n        \"x-csrf-token\": cache.get(\"pixivCsrfToken\"),\n        \"Cookie\": cache.get(\"pixivCookie\")\n    }\n    putInCache(\"headers\", headers)\n    return headers\n}\n\npublicFunc()\nif (result.code() === 200) {\n    getPixivUid(); getWebViewUA(); util.getCookie(); util.getCsrfToken(); getHeaders()\n    if (!util.settings.FAST) checkMessageThread()   \/\/ 检测过度访问\n}\n\nutil.debugFunc(() => {\n    java.log(`DEBUG = ${util.settings.DEBUG}\\n`)\n    java.log(JSON.stringify(util.settings, null, 4))\n    java.log(`${getWebViewUA()}\\n`)\n    java.log(`${cache.get(\"pixivCsrfToken\")}\\n`)\n    java.log(`${cache.get(\"pixivCookie\")}\\n`)\n    java.log(`${cache.get(\"headers\")}\\n`)\n})\n\njava.getStrResponse(null, null)",
    "loginUi": "[\n    {\n        \"name\": \"\\uD83C\\uDD7F️ 登录账号\",\n        \"type\": \"button\",\n        \"action\": \"login()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"⚙️ 账号设置\",\n        \"type\": \"button\",\n        \"action\": \"startPixivSettings()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDD19 退出账号\",\n        \"type\": \"button\",\n        \"action\": \"logout()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"🆙 更新书源\",\n        \"type\": \"button\",\n        \"action\": \"updateSource()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDD30 使用指南\",\n        \"type\": \"button\",\n        \"action\": \"startGithubReadme()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"✈\\uFE0F 直连模式\",\n        \"type\": \"button\",\n        \"action\": \"editSettings('IPDirect')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"❤️ 公开收藏\",\n        \"type\": \"button\",\n        \"action\": \"novelBookmarkFactory(1)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDCC3 追更系列\",\n        \"type\": \"button\",\n        \"action\": \"seriesWatchFactory()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"❤️ 收藏系列\",\n        \"type\": \"button\",\n        \"action\": \"novelsBookmarkAdd()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDDA4 取消收藏\",\n        \"type\": \"button\",\n        \"action\": \"novelsBookmarkDelete()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"⭐️ 关注作者\",\n        \"type\": \"button\",\n        \"action\": \"userFollowFactory()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDEAB 屏蔽作者\",\n        \"type\": \"button\",\n        \"action\": \"userBlock()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n\n    {\n        \"name\": \"输入内容\",\n        \"type\": \"text\"\n    },\n    {\n        \"name\": \"✅ 发送评论\",\n        \"type\": \"button\",\n        \"action\": \"novelCommentAdd()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDDD1 删除评论\",\n        \"type\": \"button\",\n        \"action\": \"novelCommentDelete()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83E\\uDDF9 清除缓存\",\n        \"type\": \"button\",\n        \"action\": \"cleanCache()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n\n    {\n        \"name\": \"\\uD83D\\uDEAB 添加屏蔽\",\n        \"type\": \"button\",\n        \"action\": \"blockAddFactory()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"⭕️ 删除屏蔽\",\n        \"type\": \"button\",\n        \"action\": \"blockDeleteFactory()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDC40 查看屏蔽\",\n        \"type\": \"button\",\n        \"action\": \"blockShowFactory()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n\n    {\n        \"name\": \"\\uD83D\\uDCCC 喜欢标签\",\n        \"type\": \"button\",\n        \"action\": \"likeTagsAdd()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDDD1 删除标签\",\n        \"type\": \"button\",\n        \"action\": \"likeTagsDelete()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDC40 查看标签\",\n        \"type\": \"button\",\n        \"action\": \"likeTagsShow()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n\n    {\n        \"name\": \"❤️ 他人收藏\",\n        \"type\": \"button\",\n        \"action\": \"likeAuthorsAdd()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDDA4 取消收藏\",\n        \"type\": \"button\",\n        \"action\": \"likeAuthorsDelete()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDC40 查看收藏\",\n        \"type\": \"button\",\n        \"action\": \"likeAuthorsShow()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n\n    {\n        \"name\": \"书源设置\",\n        \"type\": \"text\"\n    },\n    {\n        \"name\": \"⚙️ 当前设置\",\n        \"type\": \"button\",\n        \"action\": \"showSettings()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDD27 默认设置\",\n        \"type\": \"button\",\n        \"action\": \"editSettings('')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDC64 搜索作者\",\n        \"type\": \"button\",\n        \"action\": \"editSettings('SEARCH_AUTHOR')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83C\\uDC04 繁简通搜\",\n        \"type\": \"button\",\n        \"action\": \"editSettings('CONVERT_CHINESE')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDCD6 更多简介\",\n        \"type\": \"button\",\n        \"action\": \"editSettings('MORE_INFORMATION')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDCC5 更新时间\",\n        \"type\": \"button\",\n        \"action\": \"editSettings('SHOW_UPDATE_TIME')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDD17 原始链接\",\n        \"type\": \"button\",\n        \"action\": \"editSettings('SHOW_ORIGINAL_LINK')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDCDA 恢复《》\",\n        \"type\": \"button\",\n        \"action\": \"editSettings('REPLACE_TITLE_MARKS')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDDBC️ 显示描述\",\n        \"type\": \"button\",\n        \"action\": \"editSettings('SHOW_CAPTIONS')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDCAC 显示评论\",\n        \"type\": \"button\",\n        \"action\": \"editSettings('SHOW_COMMENTS')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"❤️ 显示收藏\",\n        \"type\": \"button\",\n        \"action\": \"editSettings('SHOW_LIKE_NOVELS')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDCC3 显示追更\",\n        \"type\": \"button\",\n        \"action\": \"editSettings('SHOW_WATCHED_SERIES')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"⏩ 快速模式\",\n        \"type\": \"button\",\n        \"action\": \"editSettings('FAST')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDC1E 调试模式\",\n        \"type\": \"button\",\n        \"action\": \"editSettings('DEBUG')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDD0D 搜索说明\",\n        \"type\": \"button\",\n        \"action\": \"readMeSearch()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"\\uD83D\\uDCC4 搜索页码\",\n        \"type\": \"button\",\n        \"action\": \"showMaxPages()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"⏫ 增加页码\",\n        \"type\": \"button\",\n        \"action\": \"editMaxPages('add')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        \"name\": \"⏬ 减少页码\",\n        \"type\": \"button\",\n        \"action\": \"editMaxPages('minus')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    }\n]",
    "loginUrl": "function login() {\n    sleepToast(\"🔄 正在检测登陆状态,请稍候\")\n    if (isLogin()) {\n        sleepToast(\"️🅿️ 登录账号\\n✅ 已经登录过账号了\\n\\n可以点击【🔙 退出账号】来切换账号\")\n        return false\n    }\n\n    let resp = java.startBrowserAwait(`https:\/\/accounts.pixiv.net\/login,\n    {\"headers\": {\"User-Agent\": ${getWebViewUA()}}}`, '登录账号', false)\n    if (resp.code() === 200) {\n        getCsrfToken(); getCookie()\n        return true\n    } else {\n        java.log(resp.code()); sleepToast(\"🅿️ 登录账号\\n\\n⚠️ 登录失败\")\n        return false\n    }\n}\n\nfunction logout() {\n    removeCookie()\n    java.startBrowser(\"https:\/\/www.pixiv.net\/logout.php\", \"退出账号\")\n    removeCookie(); removeLikeDataCache(); removeSettingsCache()\n    sleepToast(`✅ 已退出当前账号\\n\\n退出后请点击右上角的 ✔️ 退出\\n\\n登录请点击【登录账号】进行登录`)\n}\n\nfunction removeCookie() {\n    cookie.removeCookie('https:\/\/www.pixiv.net')\n    cookie.removeCookie('https:\/\/accounts.pixiv.net')\n    cookie.removeCookie('https:\/\/accounts.google.com')\n    cookie.removeCookie('https:\/\/api.weibo.com')\n    cache.delete(\"pixivCookie\")\n    cache.delete(\"pixiv:uid\")\n    cache.delete(\"pixivCsrfToken\")  \/\/ 与登录设备有关\n    cache.delete(\"headers\")\n}\n\nfunction removeCacheList(listName) {\n    let list = getFromCache(listName)\n    list.forEach(item => cache.delete(`collect${item}`))\n    if (listName !== \"blockAuthorList\") cache.delete(listName)\n}\n\nfunction removeLikeDataCache() {\n    \/\/ 删除 likeNovels 与 watchedSeries\n    removeCacheList(\"likeNovels\")\n    removeCacheList(\"watchedSeries\")\n}\n\nfunction removeSettingsCache() {\n    \/\/ 删除 自动翻页的最大页码\n    cache.delete(\"maxPagesKey\")\n    cache.delete(\"novelsMaxPages\")\n    cache.delete(\"seriesMaxPages\")\n\n    \/\/ 删除 屏蔽作者名单\n    \/\/ removeCacheList(\"blockAuthorList\")\n    \/\/ 删除  屏蔽关键词\n    \/\/ cache.delete(\"tagsBlockWords\")\n    \/\/ cache.delete(\"captionBlockWords\")\n}\n\nfunction getCookie() {\n    let pixivCookie = String(java.getCookie(\"https:\/\/www.pixiv.net\/\", null))\n    if (isLogin()) cache.put(\"pixivCookie\", pixivCookie, 60*60)\n}\n\n\/\/ 获取 Csrf Token,以便进行收藏等请求\n\/\/ 获取方法来自脚本 Pixiv Previewer\n\/\/ https:\/\/github.com\/Ocrosoft\/PixivPreviewer\n\/\/ https:\/\/greasyfork.org\/zh-CN\/scripts\/30766-pixiv-previewer\/code\nfunction getCsrfToken() {\n    let pixivCsrfToken = cache.get(\"pixivCsrfToken\")\n    if (!pixivCsrfToken) {\n        let html = java.webView(null, \"https:\/\/www.pixiv.net\/\", null)\n        try {\n            pixivCsrfToken = html.match(\/token\\\\\":\\\\\"([a-z0-9]{32})\/)[1]\n            cache.put(\"pixivCsrfToken\", pixivCsrfToken)  \/\/ 与登录设备有关,无法存储 nul\n        } catch (e) {\n            pixivCsrfToken = null\n            cache.delete(\"pixivCsrfToken\")  \/\/ 与登录设备有关,无法存储 nul\n            \/\/ sleepToast(\"⚠️ 未登录账号(pixivCsrfToken)\")\n        }\n        java.log(`pixivCsrfToken:\\n${pixivCsrfToken}`)\n    }\n    return pixivCsrfToken\n}\n\nfunction getNovel() {\n    let novel = source.getLoginInfoMap()\n    if (!novel) novel = getFromCache(\"novel\")\n    return novel\n}\n\nfunction getPostBody(url, body, headers) {\n    if (headers === undefined) headers = getFromCache(\"headers\")\n    if (isJsonString(body)) {\n        headers[\"content-type\"] = \"application\/json; charset=utf-8\"\n    } else if (typeof body === \"string\") {\n        headers[\"content-type\"] = \"application\/x-www-form-urlencoded; charset=utf-8\"\n    }\n    try {\n        java.log(`getPostBody(${url}, ${body}, ${headers})`)\n        return JSON.parse(java.post(url, body, headers).body())\n    } catch (e) {\n        e = String(e)\n        \/\/ sleepToast(e)\n        \/\/ sleepToast(JSON.stringify(headers))\n        if (e.includes(\"400\")) sleepToast(`📤 getPostBody\\n\\n⚠️ 缺少 headers`, 1)\n        else if (e.includes(\"403\")) sleepToast(`📤 getPostBody\\n\\n⚠️ 缺少 cookie 或 cookie 过期`, 1)\n        else if (e.includes(\"404\")) sleepToast(`📤 getPostBody\\n\\n⚠️ 404 缺少 pixivCsrfToken `, 1)\n        else if (e.includes(\"422\")) sleepToast(`📤 getPostBody\\n\\n⚠️ 请求信息有误`, 1)\n        return {error: true, errMsg:e}\n    }\n}\n\nfunction novelBookmarkAdd(restrict) {\n    if (restrict === undefined) restrict = 0\n    let novel = getNovel()\n    let resp = getPostBody(\n        \"https:\/\/www.pixiv.net\/ajax\/novels\/bookmarks\/add\",\n        JSON.stringify({\"novel_id\": novel.id, \"restrict\": restrict, \"comment\":\"\", \"tags\":[]})\n    )\n    if (resp.error === true) {\n        sleepToast(`❤️ 收藏小说\\n\\n⚠️ 收藏【${novel.title}】失败`)\n        shareFactory(\"novel\")\n    } else if (resp.body === null) {\n        sleepToast(`❤️ 收藏小说\\n\\n✅ 已经收藏【${novel.title}】了`)\n    } else {\n        cache.put(`collect${novel.id}`, resp.body)\n        sleepToast(`❤️ 收藏小说\\n\\n✅ 已收藏【${novel.title}】`)\n\n        let likeNovels = getFromCache(\"likeNovels\")\n        likeNovels.push(Number(novel.id))\n        putInCache(\"likeNovels\", likeNovels)\n\n        let novelObj = getAjaxJson(urlNovelDetailed(novel.id))\n        novelObj.body.isBookmark = true\n        putInCache(urlNovelDetailed(novel.id), novelObj, cacheSaveSeconds)\n    }\n}\n\nfunction getNovelBookmarkId(novelId) {\n    let bookmarkId = getFromCache(`collect${novelId}`)\n    if (bookmarkId === null) {\n        bookmarkId = getAjaxJson(urlNovelBookmarkData(novelId), true).body.bookmarkData.id\n    }\n    return bookmarkId\n}\n\nfunction novelBookmarkDelete() {\n    let novel = getNovel()\n    let resp = getPostBody(\n        \"https:\/\/www.pixiv.net\/ajax\/novels\/bookmarks\/delete\",\n        `del=1&book_id=${getNovelBookmarkId(novel.id)}`\n    )\n    if (resp.error === true) {\n        sleepToast(`❤️ 收藏小说\\n\\n⚠️ 取消收藏【${novel.title}】失败`)\n        shareFactory(\"novel\")\n    } else {\n        cache.delete(`collect${novel.id}`)\n        sleepToast(`❤️ 收藏小说\\n\\n✅ 已取消收藏【${novel.title}】`)\n\n        let likeNovels = getFromCache(\"likeNovels\")\n        likeNovels = likeNovels.filter(item => item !== Number(novel.id))\n        putInCache(\"likeNovels\", likeNovels)\n\n        let novelObj = getAjaxJson(urlNovelDetailed(novel.id))\n        novelObj.body.isBookmark = false\n        putInCache(urlNovelDetailed(novel.id), novelObj, cacheSaveSeconds)\n    }\n}\n\nfunction novelsBookmarkDelete() {\n    let novel = getNovel()\n    if (!novel.seriesId) {\n        sleepToast(`🖤 取消收藏系列\\n\\n⚠️ 【${novel.title}】非系列小说,现已取消收藏本篇小说`)\n        return novelBookmarkDelete(0)\n    } else {\n        sleepToast(`🖤 取消收藏系列\\n\\n🔄 正在取消收藏系列【${novel.seriesTitle}】,请稍后……`, 2)\n    }\n\n    let bookmarkIds = []\n    let novelIds = getFromCache(`novelIds${novel.seriesId}`)\n    novelIds.forEach(novelId => {bookmarkIds.push(getNovelBookmarkId(novelId))})\n    let resp = getPostBody(\n        \"https:\/\/www.pixiv.net\/ajax\/novels\/bookmarks\/remove\",\n        JSON.stringify({\"bookmarkIds\": bookmarkIds})\n    )\n    if (resp.error === true) {\n        sleepToast(`🖤 取消收藏系列\\n\\n⚠️ 取消收藏【${novel.seriesTitle}】的篇目失败`, 2)\n        shareFactory(\"series\")\n    } else {\n        sleepToast(`🖤 取消收藏系列\\n\\n✅ 已取消收藏【${novel.seriesTitle}】的全部篇目`)\n        novelIds.forEach(novelId => {cache.delete(`collect${novelId}`)})\n\n        let likeNovels = getFromCache(\"likeNovels\")\n        likeNovels = likeNovels.filter(item => !novelIds.includes(Number(item)))\n        putInCache(\"likeNovels\", likeNovels)\n\n        novelIds.forEach(novelId => {\n            let novelObj = getAjaxJson(urlNovelDetailed(novelId))\n            novelObj.body.isBookmark = false\n            putInCache(urlNovelDetailed(novelId), novelObj, cacheSaveSeconds)\n        })\n    }\n}\n\nfunction novelsBookmarkAdd() {\n    let novel = getNovel()\n    if (!novel.seriesId) {\n        sleepToast(`❤️ 收藏系列\\n\\n⚠️ 【${novel.title}】非系列小说,现已收藏本篇小说`)\n        return novelBookmarkAdd(0)\n    } else {\n        sleepToast(`❤️ 收藏系列\\n\\n🔄 正在收藏系列【${novel.seriesTitle}】,请稍后……`, 2)\n    }\n\n    let novelIds = getFromCache(`novelIds${novel.seriesId}`)\n    let likeNovels = getFromCache(\"likeNovels\")\n    if (likeNovels === null) likeNovels = []\n    novelIds.forEach(novelId => {\n        if (likeNovels && !likeNovels.includes(Number(novelId))) {\n            sleep(0.5 * 1000 * Math.random())\n            let resp = getPostBody(\n                \"https:\/\/www.pixiv.net\/ajax\/novels\/bookmarks\/add\",\n                JSON.stringify({\"novel_id\": novelId, \"restrict\": 0, \"comment\": \"\", \"tags\": []})\n            )\n\n            if (resp.error === true) {\n                sleepToast(`❤️ 收藏系列\\n\\n⚠️ 收藏【${novelId}】失败`)\n                shareFactory(\"series\")\n            } else if (resp.body === null) {\n                \/\/ sleepToast(`❤️ 收藏小说\\n\\n✅ 已经收藏【${novel.title}】了`)\n            } else {\n                cache.put(`collect${novelId}`, resp.body)\n                likeNovels.push(Number(novelId))\n\n                let novelObj = getAjaxJson(urlNovelDetailed(novelId))\n                novelObj.body.isBookmark = true\n                putInCache(urlNovelDetailed(novelId), novelObj, cacheSaveSeconds)\n            }\n        }\n    })\n    putInCache(\"likeNovels\", likeNovels)\n    sleepToast(`❤️ 收藏系列\\n\\n✅ 已经收藏【${novel.seriesTitle}】全部章节`)\n}\n\nfunction novelBookmarkFactory(code) {\n    let novel = getNovel()\n    let collectId = getFromCache(`collect${novel.id}`)\n    if (collectId >= 1) code = 0\n\n    if (code === 0) novelBookmarkDelete()\n    else if (code === 1) novelBookmarkAdd(0)\n    else if (code === 2) novelBookmarkAdd(1)\n}\n\nfunction novelMarker(page) {\n    if (page === undefined) page = 1\n    let novel = getNovel()\n    let lastMarker = getFromCache(`marker${novel.id}`)\n    if (lastMarker === true) page = 0\n\n    let resp = getPostBody(\n        \"https:\/\/www.pixiv.net\/novel\/rpc_marker.php\",\n        `mode=save&i_id=${novel.id}&u_id=${getFromCache(\"pixiv:uid\")}&page=${page}`\n    )\n    java.log(`mode=save&i_id=${novel.id}&u_id=${getFromCache(\"pixiv:uid\")}&page=${page}`)\n    if (resp.error === true) {\n        sleepToast(\"🏷️ 添加书签\\n\\n⚠️ 操作失败\", 1)\n        shareFactory(\"novel\")\n    } else if (lastMarker === true) {\n        cache.put(`marker${novel.id}`, false)\n        sleepToast(`🏷️ 添加书签\\n\\n✅ 已删除书签`)\n    } else {\n        cache.put(`marker${novel.id}`, true)\n        sleepToast(`🏷️ 添加书签\\n\\n✅ 已加入书签`)\n    }\n}\n\nfunction seriesWatch() {\n    let novel = getNovel()\n    let resp = getPostBody(\n        `https:\/\/www.pixiv.net\/ajax\/novel\/series\/${novel.seriesId}\/watch`,\n        \"{}\"\n    )\n    if (resp.error === true) {\n        sleepToast(`📃 追更系列\\n\\n⚠️ 追更【${novel.seriesTitle}】失败`, 1)\n        shareFactory(\"series\")\n    } else {\n        cache.put(`watch${novel.seriesId}`, true)\n        sleepToast(`📃 追更系列\\n\\n✅ 已追更【${novel.seriesTitle}】`)\n\n        let watchedSeries = getFromCache(\"watchedSeries\")\n        watchedSeries.push(Number(novel.seriesId))\n        putInCache(\"watchedSeries\", watchedSeries)\n\n        let novelObj = getAjaxJson(urlSeriesDetailed(novel.seriesId))\n        novelObj.body.isWatched = true\n        putInCache(urlSeriesDetailed(novel.seriesId), novelObj, cacheSaveSeconds)\n    }\n}\n\nfunction seriesUnWatch() {\n    let novel = getNovel()\n    let resp = getPostBody(\n        `https:\/\/www.pixiv.net\/ajax\/novel\/series\/${novel.seriesId}\/unwatch`,\n        \"{}\"\n    )\n    if (resp.error === true) {\n        sleepToast(`📃 追更系列\\n\\n⚠️ 取消追更【${novel.seriesTitle}】失败`, 1)\n        shareFactory(\"series\")\n    } else {\n        cache.delete(`watch${novel.seriesId}`)\n        sleepToast(`📃 追更系列\\n\\n✅ 已取消追更【${novel.seriesTitle}】`)\n\n        let watchedSeries = getFromCache(\"watchedSeries\")\n        watchedSeries = watchedSeries.filter(item => item !== Number(novel.seriesId))\n        putInCache(\"watchedSeries\", watchedSeries)\n\n        let novelObj = getAjaxJson(urlSeriesDetailed(novel.seriesId))\n        novelObj.body.isWatched = false\n        putInCache(urlSeriesDetailed(novel.seriesId), novelObj, cacheSaveSeconds)\n    }\n}\n\nfunction seriesWatchFactory(code) {\n    if (code === undefined) code = 1\n    let novel = getNovel()\n    if (!novel.seriesId) {\n        return sleepToast(`📃 追更系列\\n\\n⚠️ 【${novel.title}】非系列小说,无法加入追更列表`)\n    }\n\n    let lastStatus = getFromCache(`watch${novel.seriesId}`)\n    if (lastStatus === true) code = 0\n    if (code === 0) seriesUnWatch()\n    else if (code === 1) seriesWatch()\n}\n\nfunction userFollow(restrict) {\n    if (restrict === undefined) restrict = 0\n    let novel = getNovel()\n    let resp = getPostBody(\n        \"https:\/\/www.pixiv.net\/bookmark_add.php\",\n        `mode=add&type=user&user_id=${novel.userId}&tag=\"\"&restrict=${restrict}&format=json`\n    )\n    if (resp.error === true) {\n        sleepToast(`⭐️ 关注作者\\n\\n⚠️ 关注【${novel.userName}】失败`, 1)\n        shareFactory(\"author\")\n    } else {\n        sleepToast(`⭐️ 关注作者\\n\\n✅ 已关注【${novel.userName}】`)\n        cache.put(`follow${novel.userId}`, true)\n    }\n}\n\nfunction userUnFollow() {\n    let novel = getNovel()\n    let resp = getPostBody(\n        \"https:\/\/www.pixiv.net\/rpc_group_setting.php\",\n        `mode=del&type=bookuser&id=${novel.userId}`\n    )\n    if (resp.error === true) {\n        sleepToast(`⭐️ 关注作者\\n\\n⚠️ 取消关注【${novel.userName}】失败`, 1)\n        shareFactory(\"author\")\n    } else {\n        sleepToast(`⭐️ 关注作者\\n\\n✅ 已取消关注【${novel.userName}】`)\n        cache.delete(`follow${novel.userId}`)\n    }\n}\n\nfunction userFollowFactory(code) {\n    if (code === undefined) code = 1\n    let novel = getNovel()\n    let lastStatus = getFromCache(`follow${novel.userId}`)\n    if (lastStatus === true) code = 0\n\n    if (code === 0) userUnFollow()\n    else if (code === 1) userFollow()\n}\n\nfunction userBlackList() {\n    let action = \"block\"  \/\/ 拉黑作者,非屏蔽作者作品\n    let novel = getNovel()\n    let lastStatus = getFromCache(`block${novel.userId}`)\n    if (lastStatus === true) action = \"unblock\"\n\n    let resp = getPostBody(\n        `https:\/\/www.pixiv.net\/ajax\/block\/save`,\n        JSON.stringify({\"user_id\": novel.userId, \"action\": action})\n    )\n    \/\/ java.log(JSON.stringify({\"user_id\": novel.userId, \"action\": action}))\n    if (resp.error === true) sleepToast(\"⚠️ 操作失败\", 1)\n    else if (lastStatus === true) {\n        cache.put(`block${novel.userId}`, false)\n        sleepToast(`✅ 已取消拉黑【${novel.userName}】\\n\\n已允许其点赞、评论、收藏、关注、私信等`)\n    } else {\n        cache.put(`block${novel.userId}`, true)\n        sleepToast(`✅ 已拉黑【${novel.userName}】(Pixiv)\\n\\n已禁止其点赞、评论、收藏、关注、私信等`)\n    }\n}\n\nfunction userBlock() {\n    let authors = getFromCache(\"blockAuthorList\")\n    if (!authors) authors = []\n    let authorsMap = getFromCacheMap(\"blockAuthorMap\")\n    if (!authorsMap || authorsMap.size === 0) {\n        authorsMap = new Map()\n        authors.forEach(author => {\n            authorsMap.set(author, getAjaxJson(urlUserDetailed(author)).body.name)\n        })\n    }\n\n    let novel = getNovel()\n    if (authorsMap.has(String(novel.userId))) {\n        authorsMap.delete(String(novel.userId))\n        sleepToast(`🚫 屏蔽作者\\n\\n✅ 已取消屏蔽【${novel.userName}】\\n现已恢复显示其小说`)\n    } else if (!!novel.userId) {\n        authorsMap.set(String(novel.userId), novel.userName)\n        sleepToast(`🚫 屏蔽作者\\n\\n✅ 本地已屏蔽【${novel.userName}】\\n今后不再显示其小说`)\n    }\n\n    authors = Array.from(authorsMap.keys())\n    putInCache(\"blockAuthorList\", authors)\n    putInCacheMap(\"blockAuthorMap\", authorsMap)\n    \/\/ source.setVariable(authors.toString())\n    \/\/ sleepToast(JSON.stringify(authors))\n}\n\nfunction novelCommentAdd() {\n    let resp, novel = getNovel()\n    let userId = getFromCache(\"pixiv:uid\")\n    let comment = String(result.get(\"输入内容\")).trim()\n    if (comment === \"\") {\n        return sleepToast(`✅ 发送评论\\n⚠️ 请在【输入内容】输入评论\\n\\n输入【评论内容;评论ID】可回复该条评论,如【非常喜欢;123456】\\n\\n📌 当前章节:${novel.title}\\n如非当前章节,请刷新正文`)\n    }\n\n    let matched = comment.match(RegExp(\/(;|;\\s*)\\d{8,}\/))\n    if (matched) {\n        let commentId = comment.match(new RegExp(\/;(\\d{8,})\/))[1]\n        comment = comment.replace(new RegExp(`(;|;\\s*)${commentId}`), \"\")\n        resp = getPostBody(\n            \"https:\/\/www.pixiv.net\/novel\/rpc\/post_comment.php\",\n            `type=comment&novel_id=${novel.id}&author_user_id=${userId}&comment=${encodeURI(comment)}&parent_id=${commentId}`)\n    } else {\n        resp = getPostBody(\n            \"https:\/\/www.pixiv.net\/novel\/rpc\/post_comment.php\",\n            `type=comment&novel_id=${novel.id}&author_user_id=${userId}&comment=${encodeURI(comment)}`\n        )\n    }\n\n    if (resp.error === true) {\n        sleepToast(\"✅ 发送评论\\n\\n⚠️ 评论失败\", 1)\n        shareFactory(\"novel\")\n    } else {\n        sleepToast(`✅ 发送评论\\n\\n✅ 已在【${novel.title}】发布评论:\\n${comment}`)\n    }\n}\n\nfunction getNovelCommentID(novelId, commentText) {\n    let list = [], uid = String(getFromCache(\"pixiv:uid\"))\n    let resp = getAjaxJson(urlNovelComments(novelId, 0, 50), true)\n    resp.body.comments.forEach(comment => {\n        if (comment.userId === uid && comment.comment === commentText) list.push(comment.id)\n\n        if (comment.hasReplies === true) {\n            let resp = getAjaxJson(urlNovelCommentsReply(comment.id, 1), true)\n            resp.body.comments.forEach(comment => {\n                if (comment.userId === uid && comment.comment === commentText) list.push(comment.id)\n            })\n        }\n    })\n    \/\/ java.log(JSON.stringify(list))\n    return list\n}\n\nfunction novelCommentDelete() {\n    let commentIDs, novel = getNovel()\n    let comment = String(result.get(\"输入内容\")).trim()\n    if (comment === \"\") {\n        return sleepToast(`🗑 删除评论\\n⚠️ 请在【输入内容】输入需要删除的【评论ID】\\n或输入需要删除的【评论内容】\\n\\n📌 当前章节:${novel.title}\\n如非当前章节,请刷新正文`)\n    }\n\n    let matched = comment.match(RegExp(\/\\d{8,}\/))\n    if (matched) {\n        commentIDs = [matched[0]]\n    } else {\n        commentIDs = getNovelCommentID(novel.id, comment)\n        java.log(JSON.stringify(commentIDs))\n        if (commentIDs.length === 0) {\n            return sleepToast(`🗑 删除评论\\n\\n⚠️ 未能找到这条评论\\n请检查是否有错别字或标点符号是否一致`)\n        }\n    }\n\n    commentIDs.forEach(commentID =>{\n        let resp = getPostBody(\n            \"https:\/\/www.pixiv.net\/novel\/rpc_delete_comment.php\",\n            `i_id=${novel.id}&del_id=${commentID}`\n        )\n        \/\/ java.log(JSON.stringify(resp))\n        if (resp.error === true) {\n            sleepToast(\"🗑 删除评论\\n\\n⚠️ 评论删除失败\", 1)\n            shareFactory(\"novel\")\n        } else {\n            sleepToast(`🗑 删除评论\\n\\n✅ 已在【${novel.title}】删除评论:\\n${comment}`)\n        }\n    })\n}\n\nfunction novelPollAnswer() {\n    let novel = getNovel()\n    \/\/ novel.pollChoicesCount = getAjaxJson(urlNovelDetailed(novel.id)).body.pollData.selectedValue\n    if (!novel.pollChoicesCount) {\n        return sleepToast(`📃 小说投票\\n\\n⚠️ 该小说【${novel.title}】无投票信息,建议【清除缓存】【刷新】后重试`)\n    }\n\n    let choiceId = String(result.get(\"输入内容\")).trim()\n    if (!choiceId) {\n        return sleepToast(`📃 小说投票\\n\\n⚠️ 投票失败:请在【输入内容】输入投票选项(数字)`)\n    } else if (Number(choiceId) > novel.pollData.selectedValue) {\n        return sleepToast(`📃 小说投票\\n\\n⚠️ 投票失败:选项${choiceId}超出范围`)\n    } else if (Number(choiceId) <= 0 || Number(choiceId) > novel.pollChoicesCount) {\n        return sleepToast(`📃 小说投票\\n\\n⚠️ 投票失败:选项${choiceId}超出范围`)\n    }\n\n    let resp = getPostBody(\n        `https:\/\/www.pixiv.net\/ajax\/novel\/${novel.id}\/poll\/answer`,\n        JSON.stringify({\"choice_id\": choiceId})\n    )\n    \/\/ 200 成功,403 重复投票,400 选项超过范围\n    if (resp.error === true) {\n        if (resp.errMsg.includes(\"403\")) {\n            sleepToast(`📃 小说投票\\n\\n✅ 已经投过票了`)\n        } else {\n            sleepToast(`📃 小说投票\\n\\n⚠️ 投票失败`)\n            shareFactory(\"novel\")\n        }\n    } else {\n        sleepToast(`📃 小说投票\\n\\n✅ 投票成功`)\n    }\n}\n\nlet wordsType = {\n    \"caption\": \"📃 简介屏蔽列表\",\n    \"tags\": \"#️ 标签屏蔽列表\",\n    \"authors\": \"👤 作者屏蔽列表\"\n}\n\nfunction printAuthorMap(map) {\n    let text = \"\"\n    map.forEach((value, key) => {\n        text += `@${value} ${key}\\n`\n    })\n    return text.trim()\n}\n\nfunction blockShowFactory() {\n    let keys = Object.keys(wordsType)\n    let key = getFromCache(\"wordsType\")\n\n    \/\/ 切换屏蔽列表\n    let index = keys.indexOf(key) + 1\n    if (index === keys.length) index = 0\n    key = keys[index]\n    putInCache(\"wordsType\", key)\n\n    if (key === \"authors\") {\n        let words = printAuthorMap(getFromCacheMap(\"blockAuthorMap\"))\n        if (!words) words = \"\"\n        sleepToast(`👀 查看屏蔽\\n${wordsType[key]}\\n\\n${words}`, 2)\n    } else {\n        let words = getFromCache(`${key}BlockWords`)\n        if (!words) words = []\n        sleepToast(`👀 查看屏蔽\\n${wordsType[key]}\\n\\n${words.join(\"\\n\")}`, 2)\n    }\n}\n\nfunction blockWordAdd() {\n    let method = getFromCache(\"wordsType\")\n    let blockWords = getFromCache(`${method}BlockWords`)\n    if (blockWords === null) blockWords = []\n\n    let word = String(result.get(\"输入内容\")).trim()\n    if (word === \"\") {\n        sleepToast(`🚫 添加屏蔽\\n${wordsType[method]}\\n\\n⚠️ 输入内容不能为空`)\n    } else if (blockWords.includes(word)) {\n        sleepToast(`🚫 添加屏蔽\\n${wordsType[method]}\\n\\n✅ 【${word}】已经加入屏蔽列表了`)\n    } else {\n        blockWords.push(word)\n        putInCache(`${method}BlockWords`, blockWords)\n        sleepToast(`🚫 添加屏蔽\\n${wordsType[method]}\\n\\n✅ 已将【${word}】加入屏蔽列表中`)\n    }\n}\n\nfunction blockWordDelete() {\n    let method = getFromCache(\"wordsType\")\n    let blockWords = getFromCache(`${method}BlockWords`)\n    if (blockWords === null) blockWords = []\n\n    let word = String(result.get(\"输入内容\")).trim()\n    if (word === \"\") {\n        sleepToast(`⭕️ 删除屏蔽\\n${wordsType[method]}\\n\\n⚠️ 输入内容不能为空`)\n    } else if (!blockWords.includes(word)) {\n        sleepToast(`⭕️ 删除屏蔽\\n${wordsType[method]}\\n\\n⚠️ 【${word}】不在屏蔽列表\\n请检查是否有错别字或标点符号是否一致`)\n    } else {\n        blockWords = blockWords.filter(item => item !== word)\n        putInCache(`${method}BlockWords`, blockWords)\n        sleepToast(`⭕️ 删除屏蔽\\n${wordsType[method]}\\n\\n✅ 已删除屏蔽词【${word}】`)\n    }\n}\n\nfunction blockAuthorAdd() {\n    let method = getFromCache(\"wordsType\")\n    let blockAuthors = getFromCacheMap(`blockAuthorMap`)\n\n    let word = String(result.get(\"输入内容\")).trim()\n    if (word === \"\") {\n        sleepToast(`🚫 添加屏蔽\\n${wordsType[method]}\\n\\n⚠️ 输入内容不能为空\\n⚠️ 输入【用户ID】可屏蔽该作者`)\n    } else if (blockAuthors.has(word)) {\n        let text = `${blockAuthors.get(word)} ${word}`\n        sleepToast(`🚫 添加屏蔽\\n${wordsType[method]}\\n\\n✅ 【${text}】已经加入屏蔽列表了`)\n    }\n    \/\/ 输入纯数字,添加对应ID的作者\n    else if (!isNaN(word)) {\n        let user = getAjaxJson(urlUserDetailed(word)).body\n        blockAuthors.set(user.userId, user.name)\n        let text = `@${user.name} ${user.userId}`\n        sleepToast(`🚫 添加屏蔽\\n${wordsType[method]}\\n\\n✅ 已将【${text}】加入屏蔽列表中`)\n    }\n    else if (word) {\n        sleepToast(`🚫 添加屏蔽\\n${wordsType[method]}\\n\\n⚠️ 输入【用户ID】可屏蔽该作者`)\n    }\n    putInCacheMap(`blockAuthorMap`, blockAuthors)\n}\n\nfunction blockAuthorDelete() {\n    let method = getFromCache(\"wordsType\")\n    let blockAuthors = getFromCacheMap(`blockAuthorMap`)\n\n    let word = String(result.get(\"输入内容\")).trim()\n    if (word === \"\") {\n        sleepToast(`⭕️ 删除屏蔽\\n${wordsType[method]}\\n\\n⚠️ 输入内容不能为空\\n⚠️ 输入【用户ID】可屏蔽该作者`)\n    }\n    \/\/ 输入纯数字,删除对应ID的作者\n    else if (!isNaN(word) && blockAuthors.has(word)) {\n        let text = `@${blockAuthors.get(word)} ${word}`\n        blockAuthors.delete(word)\n        sleepToast(`⭕️ 删除屏蔽\\n${wordsType[method]}\\n\\n✅ 已删除【${text}】`)\n    }\n    \/\/作者名称\n    else if (Array.from(blockAuthors.values()).includes(word)) {\n        let index = Array.from(blockAuthors.values()).indexOf(word)\n        let key = Array.from(blockAuthors.keys())[index]\n        let text = `@${blockAuthors.get(key)} ${key}`\n        blockAuthors.delete(key)\n        sleepToast(`⭕️ 删除屏蔽\\n${wordsType[method]}\\n\\n✅ 已删除【${text}】`)\n    }\n    else if (word) {\n        sleepToast(`⭕️ 删除屏蔽\\n${wordsType[method]}\\n\\n⚠️ 输入【用户ID】可屏蔽该作者`)\n    }\n    putInCacheMap(`blockAuthorMap`, blockAuthors)\n}\n\nfunction blockAddFactory() {\n    if (getFromCache(\"wordsType\") === \"authors\") return blockAuthorAdd()\n    else return blockWordAdd()\n}\n\nfunction blockDeleteFactory() {\n    if (getFromCache(\"wordsType\") === \"authors\") return blockAuthorDelete()\n    else return blockWordDelete()\n}\n\n\nfunction likeTagsShow() {\n    let likeTags = getFromCache(`likeTags`)\n    if (likeTags === null) likeTags = []\n    sleepToast(`👀 查看标签\\n📌 喜欢标签\\n\\n${likeTags.join(\"、\")}`, 5)\n}\n\nfunction likeTagsAdd() {\n    let likeTags = getFromCache(`likeTags`)\n    if (likeTags === null) likeTags = []\n\n    let word = String(result.get(\"输入内容\")).trim()\n    if (word === \"\") {\n        sleepToast(`📌 添加标签\\n📌 喜欢标签\\n\\n⚠️ 输入内容不能为空\\n请直接输入标签内容`)\n    } else if (word.startsWith(\"@\") || word.startsWith(\"@\")) {\n        sleepToast(`📌 添加标签\\n📌 喜欢标签\\n\\n⚠️ 仅支持添加【标签】\\n不支持添加 @作者名称`)\n    } else if (word.startsWith(\"#\") || word.startsWith(\"#\")) {\n        sleepToast(`📌 添加标签\\n📌 喜欢标签\\n\\n⚠️ 仅支持添加【标签】\\n不支持添加 #标签名称`)\n    } else if (likeTags.includes(word)) {\n        sleepToast(`📌 添加标签\\n📌 喜欢标签\\n\\n✅ 【${word}】已经加入喜欢标签了\\n请于发现页刷新后查看`)\n    } else {\n        likeTags.push(word)\n        putInCache(`likeTags`, likeTags)\n        sleepToast(`📌 添加标签\\n📌 喜欢标签\\n\\n✅ 已将【${word}】加入喜欢标签了\\n请于发现页刷新后查看`)\n    }\n}\n\nfunction likeTagsDelete() {\n    let likeTags = getFromCache(`likeTags`)\n    if (likeTags === null) likeTags = []\n\n    let word = String(result.get(\"输入内容\")).trim()\n    if (word === \"\") {\n        sleepToast(`🗑 删除标签\\n\\n⚠️ 输入内容不能为空`)\n    } else if (!likeTags.includes(word)) {\n        sleepToast(`🗑 删除标签\\n\\n⚠️ 【${word}】不在喜欢标签\\n请检查是否有错别字`)\n    } else {\n        likeTags = likeTags.filter(item => item !== word)\n        putInCache(`likeTags`, likeTags)\n        sleepToast(`🗑 删除标签\\n\\n✅ 已删除该标签【${word}】`)\n    }\n}\n\n\nfunction likeAuthorsShow() {\n    let text = printAuthorMap(getFromCacheMap(`likeAuthors`))\n    sleepToast(`👀 查看收藏\\n❤️ 他人收藏\\n\\n${text.trim()}`, 2)\n}\n\nfunction likeAuthorsAdd() {\n    let likeAuthors = getFromCacheMap(`likeAuthors`)\n    let word = String(result.get(\"输入内容\")).trim()\n    if (word.startsWith(\"@\") || word.startsWith(\"@\")) {\n        return sleepToast(`❤️ 添加收藏\\n❤️ 他人收藏\\n\\n⚠️ 仅支持通过【作者ID】关注\\n不支持添加 @作者名称`)\n    } else if (word.startsWith(\"#\") || word.startsWith(\"#\")) {\n        return sleepToast(`❤️ 添加收藏\\n❤️ 他人收藏\\n\\n⚠️ 仅支持通过【作者ID】关注\\n不支持添加 #标签名称`)\n    } else if (likeAuthors.has(word)) {\n        let text = `${likeAuthors.get(word)} ${word}`\n        sleepToast(`❤️ 添加收藏\\n❤️ 他人收藏\\n\\n✅ 【${text}】已经加入收藏列表了,请于发现页刷新后查看`)\n    }\n\n    \/\/ 无输入内容,添加当前小说的作者\n    if (word === \"\") {\n        let novel = getNovel()\n        likeAuthors.set(String(novel.userId), novel.userName)\n        let text = `@${novel.userName} ${novel.userId}`\n        sleepToast(`❤️ 添加收藏\\n❤️ 他人收藏\\n\\n✅ 已将【${text}】加入收藏列表了,请于发现页刷新后查看\\n\\n⚠️ 输入【用户ID】可关注其他用户的收藏\\n默认关注当前作者(用户)`)\n    }\n    \/\/ 输入纯数字,添加对应ID的作者\n    else if (!isNaN(word)) {\n        let user = getAjaxJson(urlUserDetailed(word)).body\n        likeAuthors.set(user.userId, user.name)\n        let text = `@${user.name} ${user.userId}`\n        sleepToast(`❤️ 添加收藏\\n❤️ 他人收藏\\n\\n✅ 已将【${text}】加入收藏列表了,请于发现页刷新后查看`)\n    }\n\n    else if (word) {\n        sleepToast(`❤️ 添加收藏\\n❤️ 他人收藏\\n\\n⚠️ 输入【用户ID】可关注其他用户的收藏`)\n    }\n    putInCacheMap(`likeAuthors`, likeAuthors)\n}\n\nfunction likeAuthorsDelete() {\n    let likeAuthors = getFromCacheMap(`likeAuthors`)\n    let word = String(result.get(\"输入内容\")).trim()\n    if (word.startsWith(\"@\") || word.startsWith(\"@\")) {\n        return sleepToast(`🖤 取消收藏\\n❤️ 他人收藏\\n\\n⚠️ 仅支持通过【作者ID\/作者名称】取关\\n不支持输入 @作者名称`)\n    } else if (word.startsWith(\"#\") || word.startsWith(\"#\")) {\n        return sleepToast(`🖤 取消收藏\\n❤️ 他人收藏\\n\\n⚠️ 仅支持通过【作者ID\/作者名称】取关\\n不支持输入 #标签名称`)\n    }\n\n    if (word === \"\") {\n        let novel = getNovel()\n        likeAuthors.delete(novel.userId)\n        let text = `@${novel.userName} ${novel.userId}`\n        sleepToast(`🖤 取消收藏\\n❤️ 他人收藏\\n\\n✅ 已取关【${text}】\\n\\n输入【用户ID】可取关其他用户\\n默认取关当前作者(用户)`)\n\n    \/\/ 输入纯数字,删除对应ID的作者\n    } else if (!isNaN(word) && likeAuthors.has(word)) {\n        let text = `@${likeAuthors.get(word)} ${word}`\n        likeAuthors.delete(word)\n        sleepToast(`🖤 取消收藏\\n❤️ 他人收藏\\n\\n✅ 已取关【${text}】`)\n\n    \/\/作者名称\n    } else if (Array.from(likeAuthors.values()).includes(word)) {\n        let index = Array.from(likeAuthors.values()).indexOf(word)\n        let key = Array.from(likeAuthors.keys())[index]\n        let text = `@${likeAuthors.get(key)} ${key}`\n        likeAuthors.delete(key)\n        sleepToast(`🖤 取消收藏\\n❤️ 他人收藏\\n\\n✅ 已取关【${text}】`)\n    }\n    else if (word) {\n        sleepToast(`🖤 取消收藏\\n❤️ 他人收藏\\n\\n⚠️ 输入【用户ID】可取关其他用户的收藏`)\n    }\n    putInCacheMap(`likeAuthors`, likeAuthors)\n}\n\n\nfunction startBrowser(url, title) {\n    let msg = \"\", headers = `{\"headers\": {\"User-Agent\":\"${getWebViewUA()}\"}}`\n    if (url.includes(\"https:\/\/www.pixiv.net\")) {\n        if (url.includes(\"settings\")) msg += \"⚙️ 账号设置\"\n        else msg += \"⤴️ 分享小说\"\n        msg += \"\\n\\n即将打开 Pixiv\\n请确认已开启代理\/梯子\/VPN等\"\n    } else if (url.includes(\"https:\/\/github.com\")) {\n        if (url.includes(\"issues\")) msg += \"🐞 反馈问题\"\n        else if (url.includes(\"doc\")) msg += \"🔰 使用指南\"\n        else msg += \"⭐️ 收藏项目\"\n        msg += \"\\n\\n即将打开 Github\\n请确认已开启代理\/梯子\/VPN等\"\n    }\n    sleepToast(msg, 0.01)\n    java.startBrowser(`${url}, ${headers}`, title)\n}\n\nfunction shareFactory(type) {\n    let novel = getNovel()\n    if (novel === undefined || novel === null) return sleepToast(\"⚠️ 请在小说阅读页面,使用本功能\")\n    if (type.includes(\"author\")) {\n        startBrowser(urlUserUrl(novel.userId), novel.userName)\n    }\n    else if (type.includes(\"novel\") || (!novel.seriesId)) {\n        startBrowser(urlNovelUrl(novel.id), novel.title)\n    }\n    else if (type.includes(\"series\") && novel.seriesId) {\n        startBrowser(urlSeriesUrl(novel.seriesId), novel.seriesTitle)\n    }\n}\n\nfunction startPixivSettings() {\n    startBrowser(\"https:\/\/www.pixiv.net\/settings\/viewing\", \"账号设置\")\n}\nfunction startGithubIssue() {\n    startBrowser(\"https:\/\/github.com\/DowneyRem\/PixivSource\/issues\", \"反馈问题\")\n}\nfunction startGithubReadme() {\n    startBrowser(\"https:\/\/github.com\/DowneyRem\/PixivSource\/blob\/main\/doc\/Pixiv.md\", \"使用指南\")\n}\n\nfunction checkStatus(status) {\n    if (eval(String(status)) === true) return \"❤️\"\n    else return \"🖤\"\n}\n\nfunction charpterReading() {\n    let novel = getNovel()\n    \/\/ let novel = source.getLoginInfoMap()\n    let msg = `📌 当前章节\\n\\n${checkStatus(novel.isWatched)} 系列:${novel.seriesTitle}\\n${checkStatus(novel.isBookmark)} 章节:${novel.title}\\n👤 作者:${novel.userName}\\n\\n如非当前章节,请刷新正文`\n    msg = msg.replace(\"🖤 系列:\\n\", \"\")\n    sleepToast(msg, 2)\n}\n\nfunction readMeLogin() {\n    return sleepToast(`🅿️ 登录界面功能\\n\n    使用收藏、追更、关注作者、评论等功能时,需要登录\n    使用前请先刷新正文,获取当前章节信息\\n\n    点击【📌 当前章节】查看书源内部章节信息`.replace(\"    \",\"\"), 5)\n}\n\nfunction readMeSearch() {\n    return sleepToast(`🔍 搜索说明\\n\n    标签之间需要以【空格】间隔\n    ➖ 排除标签:#标签1 -标签2\n    👤 作者专搜:@搜索作者名称\n    #️ 标签专搜:#标签1 标签2 \n    ⏬ 字数筛选1:#标签1 标签2 字数3k5\n    ⏬ 字数筛选2:@作者的名称 字数3w5`.replace(\"    \",\"\"), 5)\n}\n\nlet settingsName = {\n    \"SEARCH_AUTHOR\": \"🔍 搜索作者\",\n    \"CONVERT_CHINESE\": \"🀄️ 繁简通搜\",\n    \"SHOW_UPDATE_TIME\": \"📅 更新时间\",\n    \"SHOW_ORIGINAL_LINK\": \"🔗 原始链接\",\n    \"SHOW_COMMENTS\": \"💬 显示评论\",\n    \"MORE_INFORMATION\": \"📖 更多简介\",\n    \"REPLACE_TITLE_MARKS\": \"📚 恢复《》\",\n    \"SHOW_CAPTIONS\": \"🖼️ 显示描述\",\n    \"SHOW_LIKE_NOVELS\" :\"❤️ 显示收藏\",\n    \"SHOW_WATCHED_SERIES\" :\"📃 显示追更\",\n    \"IPDirect\": \"✈️ 直连模式\",\n    \"FAST\": \"⏩ 快速模式\",\n    \"DEBUG\": \"🐞 调试模式\",\n    \/\/ \"\":\"Pixiv 设置\",\n    \/\/ \"HIDE_AI_WORKS\":\"隐藏AI作品\",\n    \/\/ \"SENSITIVE_VIEW\":\"敏感作品\",\n    \/\/ \"USER_X_RESTRICT\":\"成人设置\",\n    \/\/ \"READING_STATUS\":\"阅读进度\",\n}\n\nfunction getPixivSettings() {\n    let settings = getFromCache(\"pixivSettings\")\n    let resp = getAjaxJson(\"https:\/\/www.pixiv.net\/ajax\/settings\/self\")\n    if (resp.error !== true) {\n        let siteSettings = resp.body.user_status\n        settings.HIDE_AI_WORKS = siteSettings.hide_ai_works\n        settings.SENSITIVE_VIEW = siteSettings.sensitive_view_setting\n        settings.USER_X_RESTRICT = siteSettings.user_x_restrict\n        settings.READING_STATUS = siteSettings.reading_status_enabled\n    } else {\n        settings.HIDE_AI_WORKS = false\n        settings.SENSITIVE_VIEW = 0\n        settings.USER_X_RESTRICT = 0\n        settings.READING_STATUS = false\n    }\n    putInCache(\"pixivSettings\", settings)\n    return settings\n}\n\nfunction editPixivSettingsHideAI() {\n    let settings = getPixivSettings()\n    \/\/ let settings = getFromCache(\"pixivSettings\")\n    let hideAiWorks = Number(!settings.HIDE_AI_WORKS)\n    let resp = getPostBody(\n        \"https:\/\/www.pixiv.net\/ajax\/settings\/self?lang=zh\",\n        {\"hideAiWorks\": hideAiWorks}\n    )\n\n    if (resp.error === true) sleepToast(`⚠️ 隐藏AI作品 失败`, 1)\n    else if (hideAiWorks === 1) sleepToast(`⚠️ 隐藏AI作品\\n\\n✅ 已 隐藏AI作品`)\n    else sleepToast(`⚠️ 隐藏AI作品\\n\\n✅ 已取消 隐藏AI作品`)\n    settings.HIDE_AI_WORKS = Boolean(hideAiWorks)\n    putInCache(\"pixivSettings\", settings)\n}\n\nfunction editPixivSettingsXRestrict() {\n    let settings = getPixivSettings()\n    \/\/ let settings = getFromCache(\"pixivSettings\")\n    let userXRestrict = settings.USER_X_RESTRICT + 1\n    if (userXRestrict === 3) userXRestrict = 0\n    let resp = getPostBody(\n        \"https:\/\/www.pixiv.net\/ajax\/settings\/user_x_restrict\",\n        {\"userXRestrict\": userXRestrict}\n    )\n\n    if (resp.error === true) sleepToast(`⚠️ 成人作品 失败`, 1)\n    else if (hideAiWorks === 0) sleepToast(`⚠️ 成人作品\\n\\n✅ 已关闭 成人作品`)\n    else if (hideAiWorks === 1) sleepToast(`⚠️ 成人作品\\n\\n✅ 已开启 R-18作品`)\n    else sleepToast(`⚠️ 成人作品\\n\\n✅ 已开启 R-18G作品`)\n    settings.HIDE_AI_WORKS = userXRestrict\n    putInCache(\"pixivSettings\", settings)\n}\n\nfunction editPixivSettingsSensitiveView() {\n    let settings = getPixivSettings()\n    \/\/ let settings = getFromCache(\"pixivSettings\")\n    let sensitiveView = Number(!settings.SENSITIVE_VIEW)\n    let resp = getPostBody(\n        \"https:\/\/www.pixiv.net\/ajax\/settings\/sensitive_view_setting\",\n        {\"sensitiveViewSetting\": sensitiveView}\n    )\n\n    if (resp.error === true) sleepToast(`⚠️ 敏感作品 失败`, 1)\n    else if (sensitiveView === 0) {sleepToast(`⚠️ 敏感作品\\n\\n✅ 已隐藏 敏感作品`)}\n    else sleepToast(`⚠️ 敏感作品\\n\\n✅ 已显示 敏感作品`)\n    settings.SENSITIVE_VIEW = sensitiveView\n    putInCache(\"pixivSettings\", settings)\n}\n\nfunction statusMsg(status) {\n    if (status === true) return \"✅ 已开启\"\n    else if (status === false) return \"🚫 已关闭\"\n    else return \"🈚️ 未设置\"\n}\n\n\/\/ 检测快速模式修改的4个设置\nfunction getSettingStatus(mode) {\n    if (mode === undefined) mode = \"\"\n    let keys = [], msgList = []\n    let settings = getFromCache(\"pixivSettings\")\n    if (mode === \"FAST\") {\n        keys = Object.keys(settingsName).slice(0, 5)\n    } else if (mode === \"IPDirect\") {\n        keys = [Object.keys(settingsName)[0], Object.keys(settingsName)[3]]\n    } else {\n        keys = Object.keys(settingsName)\n    }\n    for (let i in keys) {\n        msgList.push(`${statusMsg(settings[keys[i]])} ${settingsName[keys[i]]}`)\n    }\n    return msgList.join(\"\\n\").trim()\n}\n\nfunction showSettings() {\n    sleepToast(`⚙️ 当前设置\\n\\n${getSettingStatus()}`)\n}\n\nfunction editSettings(object) {\n    let msg, status\n    let settings = getFromCache(\"pixivSettings\")\n    if (object === \"\") {\n        settings.SEARCH_AUTHOR = true       \/\/ 搜索:默认搜索作者名称\n        settings.CONVERT_CHINESE = true     \/\/ 搜索:搜索时进行繁简转换\n        settings.SHOW_LIKE_NOVELS = true    \/\/ 搜索:搜索结果显示收藏小说\n        settings.SHOW_WATCHED_SERIES = true \/\/ 搜索:搜索结果显示追整系列小说\n\n        settings.MORE_INFORMATION = false   \/\/ 详情:书籍简介显示更多信息\n        settings.SHOW_UPDATE_TIME = true    \/\/ 目录:显示更新时间,但会增加少许请求\n        settings.SHOW_ORIGINAL_LINK = true  \/\/ 目录:显示原始链接,但会增加大量请求\n\n        settings.REPLACE_TITLE_MARKS = true \/\/ 正文:注音内容为汉字时,替换为书名号\n        settings.SHOW_CAPTIONS = true       \/\/ 正文:章首显示描述\n        settings.SHOW_COMMENTS = true       \/\/ 正文:章尾显示评论\n\n        settings.IPDirect  = false          \/\/ 全局:快速模式\n        settings.FAST  = false              \/\/ 全局:快速模式\n        settings.DEBUG = false              \/\/ 全局:调试模式\n        putInCache(\"pixivSettings\", settings)\n        msg = `\\n✅ 已恢复 🔧 默认设置\\n\\n${getSettingStatus()}`\n\n    } else if (object === \"FAST\") {\n        if (settings[object] === true) {\n            settings.FAST = false                \/\/ 关闭:快速模式\n            settings.SEARCH_AUTHOR = true        \/\/ 搜索:默认搜索作者\n            settings.CONVERT_CHINESE = true      \/\/ 搜索:繁简通\n            settings.SHOW_UPDATE_TIME = true     \/\/ 目录:显示章节更新时间\n            settings.SHOW_ORIGINAL_LINK = true   \/\/ 目录:显示章节源链接\n            settings.SHOW_COMMENTS = true        \/\/ 正文:显示评论\n        } else {\n            settings.FAST = true\n            settings.SEARCH_AUTHOR = false        \/\/ 搜索:默认搜索作者\n            settings.CONVERT_CHINESE = false      \/\/ 搜索:繁简通搜\n            settings.SHOW_UPDATE_TIME = false     \/\/ 目录:显示章节更新时间\n            settings.SHOW_ORIGINAL_LINK = false   \/\/ 目录:显示章节源链接\n            settings.SHOW_COMMENTS = false        \/\/ 正文:显示评论\n\n        }\n        putInCache(\"pixivSettings\", settings)\n        let status = settings[object]\n        let message = getSettingStatus(\"FAST\")\n        msg = `\\n${statusMsg(status)} ${settingsName[object]}\\n\\n${message}`\n\n    } else if (object === \"IPDirect\") {\n        if (settings[object] === true) {\n            settings.IPDirect = false            \/\/ 关闭:直连模式\n            settings.SEARCH_AUTHOR = true        \/\/ 搜索:默认关闭搜索作者名称\n            settings.SHOW_ORIGINAL_LINK = true   \/\/ 目录:不显示章节源链接\n        } else {\n            settings.IPDirect = true\n            settings.SEARCH_AUTHOR = false       \/\/ 搜索:默认关闭搜索作者名称\n            settings.SHOW_ORIGINAL_LINK = false  \/\/ 目录:不显示章节源链接\n        }\n        putInCache(\"pixivSettings\", settings)\n        let status = settings[object]\n        let message = getSettingStatus(\"IPDirect\")\n        msg = `\\n${statusMsg(status)} ${settingsName[object]}\\n\\n${message}`\n\n    } else {\n        if (!!settings[object]) {\n            status = settings[object] = !settings[object]\n        } else {\n            status = settings[object] = true  \/\/ 无设置则默认开启\n        }\n        putInCache(\"pixivSettings\", settings)\n        msg = `${statusMsg(status)} ${settingsName[object]}`\n    }\n    sleepToast(msg)\n    putInCache(\"pixivSettings\", settings)\n}\n\nfunction cleanCache() {\n    let novel = getNovel()\n    cache.delete(`${urlNovelUrl(novel.id)}`)\n    cache.delete(`${urlNovelDetailed(novel.id)}`)\n    \/\/ cache.delete(`${urlSearchNovel(novel.title, 1)}`)\n    \/\/ if (novel.seriesId) {\n    \/\/     cache.delete(`${urlSeriesUrl(novel.seriesId)}`)\n    \/\/     cache.delete(`${urlSeriesDetailed(novel.seriesId)}`)\n    \/\/     cache.delete(`${urlSearchSeries(novel.seriesTitle, 1)}`)\n    \/\/ }\n    sleepToast(`🧹 清除缓存\\n\\n📌 当前章节:${novel.title}\\n\\n已清除本章正文缓存,刷新正文以更新`, 5)\n}\n\nlet maxPagesName = {\n    \"seriesMaxPages\": \"系列最大页码\",\n    \"novelsMaxPages\": \"单篇最大页码\"\n}\n\nfunction showMaxPages() {\n    let keys = Object.keys(maxPagesName)\n    let key = getFromCache(\"maxPagesKey\")\n\n    \/\/ 切换列表\n    let index = keys.indexOf(key) + 1\n    if (index === keys.length) index = 0\n    key = keys[index]\n    putInCache(\"maxPagesKey\", key)\n\n    return sleepToast(`📄 搜索页码\\n设置 #️⃣ 搜索标签的最大页码数\\n\n    当前${maxPagesName[keys[0]]}:${getFromCache(keys[0])}\\n当前${maxPagesName[keys[1]]}:${getFromCache(keys[1])}\\n\n    点击 ⏫ 增加页码\/ ⏬ 减少页码\\n调整【${maxPagesName[key]}】\\n\n    📌 页码越多,小说越多,速度越慢`.replace(\"    \", \"\"))\n}\n\nfunction editMaxPages(method) {\n    let msg = \"\", key = getFromCache(\"maxPagesKey\")\n    if (!key) key = Object.keys(maxPagesName)[0]\n    let maxPages = getFromCache(key)\n    if (!maxPages) maxPages = 1\n    if (method.includes(\"add\")) maxPages += 1\n    if (method.includes(\"min\")) maxPages -= 1\n\n    if (maxPages <= 1) {\n        maxPages = 1\n        msg += \"⚠️ 搜索页码不能再减小了\\n\"\n    }\n    if (maxPages >= 3) {\n        msg += \"⚠️ 搜索页码越多,搜索速度越慢\\n\"\n    }\n    if (maxPages >= 10) {\n        maxPages = 10\n        msg += \"⚠️ 搜索页码不能再增大了\\n\"\n    }\n    putInCache(`${key}`, maxPages)\n    sleepToast(`📄 搜索页码\\n\\n当前搜索【${maxPagesName[key]}】:${maxPages}\\n\\n${(msg)}`.trim())\n    return maxPages\n}",
    "respondTime": 180000,
    "ruleBookInfo": {
        "author": "userName",
        "canReName": "true",
        "coverUrl": "coverUrl",
        "init": "@js:\nvar util = objParse(String(java.get(\"util\")))\n\nfunction objParse(obj) {\n    return JSON.parse(obj, (n, v) => {\n        if (typeof v == \"string\" && v.match(\"()\")) {\n            return eval(`(${v})`)\n        }\n        return v;\n    })\n}\n\nfunction novelHandler(novel){\n    novel = util.formatNovels(util.handNovels([novel]))[0]\n    if (novel.seriesId === undefined || novel.seriesId === null) {\n        book.bookUrl = novel.detailedUrl = urlNovelUrl(novel.id)\n        book.tocUrl = novel.catalogUrl = urlIP(urlNovelDetailed(novel.id))\n    } else {\n        book.bookUrl = novel.detailedUrl = urlSeriesUrl(novel.seriesId)\n        book.tocUrl = novel.catalogUrl = urlIP(urlSeriesDetailed(novel.seriesId))\n    }\n    \/\/ 放入信息以便登陆界面使用\n    source.putLoginInfo(JSON.stringify(novel))\n    cache.put(\"novel\", JSON.stringify(novel))\n    return novel\n}\n\n(() => {\n    return novelHandler(util.getNovelResFirst(result))\n})()",
        "intro": "description",
        "kind": "tags",
        "lastChapter": "latestChapter",
        "name": "title",
        "tocUrl": "catalogUrl",
        "wordCount": "textCount"
    },
    "ruleContent": {
        "content": "@js:\nvar util = objParse(String(java.get(\"util\")))\nlet emoji = {\n    \"normal\": 101, \"surprise\": 102, \"series\": 103, \"heaven\": 104, \"happy\": 105,\n    \"excited\": 106, \"sing\": 107, \"cry\": 108, \"normal2\": 201, \"shame2\": 202,\n    \"love2\": 203, \"interesting2\": 204, \"blush2\": 205, \"fire2\": 206, \"angry2\": 207,\n    \"shine2\": 208, \"panic2\": 209, \"normal3\": 301, \"satisfaction3\": 302, \"surprise3\": 303,\n    \"smile3\": 304, \"shock3\": 305, \"gaze3\": 306, \"wink3\": 307, \"happy3\": 308,\n    \"excited3\": 309, \"love3\": 310, \"normal4\": 401, \"surprise4\": 402, \"series4\": 403,\n    \"love4\": 404, \"shine4\": 405, \"sweet4\": 406, \"shame4\": 407, \"sleep4\": 408,\n    \"heart\": 501, \"teardrop\": 502, \"star\": 503\n}\n\nfunction objParse(obj) {\n    return JSON.parse(obj, (n, v) => {\n        if (typeof v == \"string\" && v.match(\"()\")) {\n            return eval(`(${v})`)\n        }\n        return v;\n    })\n}\n\nfunction getNovelInfo(res) {\n    \/\/ 放入小说信息以便登陆界面使用\n    let novel = source.getLoginInfoMap()\n    if (novel === undefined) novel = JSON.parse(cache.get(\"novel\"))\n    if (res && res.error === true) return\n    novel.id = Number(res.id)\n    novel.title = res.title\n    novel.userId = res.userId\n    novel.userName = res.userName\n\n    if (res.bookmarkData) {\n        novel.isBookmark = true\n        cache.put(`collect${novel.id}`, res.bookmarkData.id)\n        util.saveNovels(\"likeNovels\", [Number(novel.id)])\n    } else {\n        novel.isBookmark = false\n    }\n\n    if (res.seriesNavData) {\n        novel.seriesId = Number(res.seriesNavData.seriesId)\n        novel.seriesTitle = res.seriesNavData.title\n        novel.isWatched = res.seriesNavData.isWatched\n        util.saveNovels(\"watchedSeries\", [Number(novel.seriesId)])\n    } else {\n        novel.seriesId = null\n        novel.seriesTitle = \"\"\n        novel.isWatched = false\n    }\n\n    \/\/ 系列 + 阅读,使用当前章节名称\n    if (novel.seriesId && util.environment.IS_LEGADO) {\n        let novelIds = JSON.parse(cache.get(`novelIds${novel.seriesId}`))\n        novel.id = novelIds[book.durChapterIndex]\n        novel.title = book.durChapterTitle\n\n        let bookmarkId = JSON.parse(cache.get(`collect${novel.id}`))\n        novel.isBookmark = !!bookmarkId\n    }\n\n    \/\/ 添加投票信息\n    if (res.pollData) novel.pollChoicesCount = res.pollData.choices.length\n    else novel.pollChoicesCount = 0\n    source.putLoginInfo(JSON.stringify(novel))\n    cache.put(\"novel\", JSON.stringify(novel))\n}\n\nfunction getContent(res) {\n    getNovelInfo(res)  \/\/ 放入信息以便登陆界面使用\n    let content = String(res.content)\n    \/\/ let content = \"undefined\"\n    if (content.includes(\"undefined\")) {\n        return checkContent()\n    }\n\n    \/\/ 在正文内部添加小说描述\n    if (util.settings.SHOW_CAPTIONS && res.description !== \"\") {\n        content = res.description + \"\\n\" + \"——————————\\n\".repeat(2) + content\n    }\n\n    \/\/ 获取 [uploadedimage:] 的图片链接\n    if (res.textEmbeddedImages) {\n        Object.keys(res.textEmbeddedImages).forEach((key) => {\n            content = content.replace(`[uploadedimage:${key}]`, `<img src=\"${urlCoverUrl(res.textEmbeddedImages[key].urls.original)}\">`)\n        })\n    }\n\n    \/\/ 获取 [pixivimage:] 的图片链接 [pixivimage:1234] [pixivimage:1234-1]\n    let matched = content.match(RegExp(\/\\[pixivimage:(\\d+)-?(\\d+)]\/gm))\n    if (matched) {\n        matched.forEach(pixivimage => {\n            let matched2, illustId, order = 0\n            if (pixivimage.includes(\"-\")) {\n                matched2 = pixivimage.match(RegExp(\"(\\\\d+)-(\\\\d+)\"))\n                illustId = matched2[1]; order = matched2[2]\n            } else {\n                matched2 = pixivimage.match(RegExp(\"\\\\d+\"))\n                illustId = matched2[0];\n            }\n            if (urlIllustOriginal(illustId, order)) {\n                content = content.replace(`${pixivimage}`, `<img src=\"${urlIllustOriginal(illustId, order)}\">`)\n            } else {\n                content = content.replace(`${pixivimage}`, ``)\n            }\n        })\n    }\n\n    \/\/ 替换 Pixiv 分页标记符号 [newpage]\n    matched = content.match(RegExp(\/[  ]*\\[newpage][  ]*\/gm))\n    if (matched) {\n        for (let i in matched) {\n            content = content.replace(`${matched[i]}`, `${\"<p>​<p\/>\".repeat(3)}`)\n        }\n    }\n\n    \/\/ 替换 Pixiv 章节标记符号 [chapter:]\n    matched = content.match(RegExp(\/\\[chapter:(.*?)]\/gm))\n    if (matched) {\n        for (let i in matched) {\n            let matched2 = matched[i].match(\/\\[chapter:(.*?)]\/m)\n            let chapter = matched2[1].trim()\n            content = content.replace(`${matched[i]}`, `${chapter}<p>​<p\/>`)\n        }\n    }\n\n    \/\/ 替换 Pixiv 跳转页面标记符号 [[jump:]]\n    matched = content.match(RegExp(\/\\[jump:(\\d+)]\/gm))\n    if (matched) {\n        for (let i in matched) {\n            let page = matched[i].match(\/\\d+\/)\n            content = content.replace(`${matched[i]}`, `\\n\\n跳转至第${page}节`)\n        }\n    }\n\n    \/\/ 替换 Pixiv 链接标记符号 [[jumpuri: > ]]\n    matched = content.match(RegExp(\/\\[\\[jumpuri:(.*?)>(.*?)]]\/gm))\n    if (matched) {\n        for (let i in matched) {\n            let matched2 = matched[i].match(\/\\[\\[jumpuri:(.*?)>(.*?)]]\/m)\n            let matchedText = matched2[0]\n            let urlName = matched2[1].trim()\n            let urlLink = matched2[2].trim()\n            \/\/ 阅读不支持超链接\n            \/\/content = content.replace(`${matchedText}`, `<a href=${urlLink}>${urlName}<\/a>`)\n            if (urlLink === urlName) {\n                content = content.replace(`${matchedText}`, `${urlName}`)\n            } else {\n                content = content.replace(`${matchedText}`, `${urlName}: ${urlLink}`)\n            }\n        }\n    }\n\n    \/\/ 替换 Pixiv 注音标记符号 [[rb: > ]]\n    matched = content.match(RegExp(\/\\[\\[rb:(.*?)>(.*?)]]\/gm))\n    if (matched) {\n        for (let i in matched) {\n            let matched2 = matched[i].match(\/\\[\\[rb:(.*?)>(.*?)]]\/m)\n            let matchedText = matched2[0]\n            let kanji = matched2[1].trim()\n            let kana = matched2[2].trim()\n\n            if (!util.settings.REPLACE_TITLE_MARKS) {\n                \/\/ 默认替换成(括号)\n                content = content.replace(`${matchedText}`, `${kanji}(${kana})`)\n            } else {\n                let reg = RegExp(\"[\\\\u4E00-\\\\u9FFF]+\", \"g\");\n                if (reg.test(kana)) {\n                    \/\/ kana为中文,则替换回《书名号》\n                    content = content.replace(`${matchedText}`, `${kanji}《${kana}》`)\n                } else {\n                    \/\/ 阅读不支持 <ruby> <rt> 注音\n                    \/\/ content = content.replace(`${matchedText}`, `<ruby>${kanji}<rt>${kana}<\/rt><\/ruby>`)\n                    content = content.replace(`${matchedText}`, `${kanji}(${kana})`)\n                }\n            }\n        }\n    }\n\n    \/\/ 添加投票\n    if (res.pollData) {\n        let poll = `📃 投票(✅${res.pollData.total}已投):\\n${res.pollData.question}\\n`\n        res.pollData.choices.forEach(choice => {\n            poll += `选项${choice.id}:${choice.text}(✅${choice.count})\\n`\n        })\n        content += \"\\n\" + \"——————————\\n\".repeat(2) + poll\n    }\n\n    \/\/ 添加评论\n    if (util.settings.SHOW_COMMENTS) {\n        return content + getComment(res)\n    } else {\n        return content\n    }\n}\n\nfunction getComment(res) {\n    \/\/ let resp = getAjaxJson(urlIP(urlNovelComments(res.id, 0, res.commentCount)), true)\n    const limit = 50  \/\/ 模拟 Pixiv 请求\n    let resp = {\"error\": false, \"message\": \"\", \"body\": {comments:[]} }\n    let maxPage = (res.commentCount \/ limit) + 1\n    for (let i = 0; i < maxPage; i++) {\n        let result = getAjaxJson(urlIP(urlNovelComments(res.id, i*limit, 50)), true)\n        if (result.error !== true && result.body.comments !== null) {\n            resp.body.comments = resp.body.comments.concat(result.body.comments)\n        }\n    }\n\n    \/\/ 刷新时,刷新评论,不更新正文\n    let commentCount = resp.body.comments.length\n    java.log(`【${res.title}】(${res.id}),共有${commentCount}条评论,${res.commentCount - commentCount}条回复`)\n    if (commentCount === 0) {\n        return \"\"\n    }\n\n    let comments = `💬 评论(共计${commentCount}条):\\n`\n    resp.body.comments.forEach(comment => {\n        if (comment.comment === \"\") {\n            comment.comment = `<img src=\"${urlStampUrl(comment.stampId)}\">`\n        }\n        if (Object.keys(emoji).includes(comment.comment.slice(1, -1))) {\n            comment.emojiId = emoji[comment.comment.slice(1, -1)]\n            comment.comment = `<img src=\"${urlEmojiUrl(comment.emojiId)}\">`\n        }\n        if (comment.userId === String(cache.get(\"pixiv:uid\"))) {\n            comments += `@${comment.userName}:${comment.comment}(${comment.commentDate})(${comment.id})\\n`\n        } else {\n            comments += `@${comment.userName}:${comment.comment}(${comment.commentDate})\\n`\n        }\n\n        \/\/ 获取评论回复\n        if (comment.hasReplies === true) {\n            let resp = getAjaxJson(urlIP(urlNovelCommentsReply(comment.id, 1)), true)\n            if (resp.error === true) return comments\n\n            resp.body.comments.reverse().forEach(reply => {\n                if (reply.comment === \"\") {\n                    reply.comment = `<img src=\"${urlStampUrl(reply.stampId)}\">`\n                }\n                if (Object.keys(emoji).includes(reply.comment.slice(1, -1))) {\n                    reply.emojiId = emoji[reply.comment.slice(1, -1)]\n                    reply.comment = `<img src=\"${urlEmojiUrl(reply.emojiId)}\">`\n                }\n                if (comment.userId === String(cache.get(\"pixiv:uid\"))) {\n                    comments += `@${reply.userName}(⤴️@${reply.replyToUserName}):${reply.comment}(${reply.commentDate})(${reply.id})\\n`\n                } else {\n                    comments += `@${reply.userName}(⤴️@${reply.replyToUserName}):${reply.comment}(${reply.commentDate})\\n`\n                }\n            })\n            comments += \"——————————\\n\"\n        }\n    })\n    if (comments) {\n        comments = \"\\n\" + \"——————————\\n\".repeat(2) + comments\n    }\n    return comments\n}\n\nfunction checkContent() {\n    let latestMsg = getAjaxJson(urlMessageThreadLatest(5))\n    if (latestMsg.error === true) {\n        java.log(JSON.stringify(latestMsg))\n\n    } else if (latestMsg.body.total >= 1) {\n        let msg = latestMsg.body.message_threads.filter(item => item.thread_name === \"pixiv事務局\")[0]\n        if (msg === undefined) {\n            sleepToast(`您于 ${java.timeFormat(new Date().getTime())} 触发 Pixiv 【请求限制】,建议稍候\/重新登录再继续`, 3)\n            \/\/ java.startBrowser(\"https:\/\/www.pixiv.net\", '退出登录')\n            \/\/ java.startBrowser(\"https:\/\/www.pixiv.net\/logout.php\",'退出登录')  \/\/ 不清除 WebView 缓存无法重新登录\n\n        } else if (new Date().getTime()- 1000*msg.modified_at <= 3*24*60*60*1000) { \/\/ 3*24h内提醒\n            sleepToast(`您于 ${java.timeFormat(1000*msg.modified_at)} 触发 Pixiv 【过度访问】,请修改密码并重新登录`, 3)\n            sleepToast(`${msg.latest_content}`, 5)\n            java.startBrowser(\"https:\/\/accounts.pixiv.net\/password\/change\",'修改密码')\n        }\n    }\n}\n\n(() => {\n    return getContent(util.getNovelResFirst(result))\n})()",
        "imageStyle": "DEFAULT"
    },
    "ruleExplore": {
        "author": "userName",
        "bookList": "@js:\nvar util = objParse(String(java.get(\"util\")))\nvar seriesSet = new Set();  \/\/ 存储seriesID 有BUG无法处理翻页\n\nfunction objParse(obj) {\n    return JSON.parse(obj, (n, v) => {\n        if (typeof v == \"string\" && v.match(\"()\")) {\n            return eval(`(${v})`)\n        }\n        return v;\n    })\n}\n\nfunction handlerFactory() {\n    if (baseUrl.includes(\"https:\/\/cdn.jsdelivr.net\")) {\n        return () => {updateSource(); return []}\n    }\n    if (!isLogin()) {\n        return handlerNoLogin()\n    }\n    if (baseUrl.includes(\"\/bookmark\")) {\n        return handlerBookMarks()\n    }\n    if (baseUrl.includes(\"\/top\")) {\n        return handlerRecommend()\n    }\n    if (baseUrl.includes(\"\/follow_latest\")) {\n        return handlerFollowLatest()\n    }\n    if (baseUrl.includes(\"\/watch_list\")) {\n        return handlerWatchList()\n    }\n    if (baseUrl.includes(\"\/discovery\")) {\n        return handlerDiscovery()\n    }\n    if (baseUrl.includes(\"\/new\")) {\n        return handlerDiscovery()\n    }\n    if (baseUrl.includes(\"\/commission\/\")) {\n        return handlerFollowLatest()\n    }\n    if (baseUrl.includes(\"\/user_event\/portal\")) {\n        return handlerFollowLatest()\n    }\n    if (baseUrl.includes(\"\/genre\")) {\n        return handlerWatchList()\n    }\n    \/\/ 正则匹配网址内容\n    if (baseUrl.includes(\"\/ranking\")) {\n        return handlerRanking()\n    }\n    if (baseUrl.includes(\"\/marker_all\")) {\n        return handlerRanking()\n    }\n    if (baseUrl.includes(\"\/editors_picks\")) {\n        return handlerRanking()\n    }\n    if (baseUrl.includes(\"\/ajax\/search\/novels\")) {\n        return handlerSearch()\n    }\n    if (baseUrl.startsWith(\"https:\/\/www.pixiv.net\")) {\n        return handlerRanking()\n    }\n    else {\n        return []\n    }\n}\n\nfunction handlerNoLogin() {\n    return () => {\n        sleepToast(\"⚠️ 当前未登录账号\\n\\n请登录 Pixiv 账号\", 1.5)\n        util.removeCookie(); util.login()\n        sleepToast(\"登录成功后,请重新进入发现\", 2)\n        return []\n    }\n}\n\n\/\/ 推荐小说\nfunction handlerRecommend() {\n    return () => {\n        let res = JSON.parse(result)\n        const recommend = res.body.page.recommend\n        const novels = res.body.thumbnails.novel\n        let nidSet = new Set(recommend.ids)\n        \/\/ java.log(nidSet.size)\n        let list = novels.filter(novel => nidSet.has(String(novel.id)))\n        \/\/ java.log(`过滤结果:${JSON.stringify(list)}`)\n        return util.formatNovels(util.handNovels(util.combineNovels(list)))\n    }\n}\n\n\/\/ 收藏小说,他人收藏\nfunction handlerBookMarks() {\n    return () => {\n        let res = JSON.parse(result).body.works\n        if (res === undefined || res.length === 0) {\n            \/\/流程无法本环节中止 只能交给下一流程处理\n            return []\n        }\n        return util.formatNovels(util.handNovels(res))\n    }\n}\n\n\/\/关注作者,小说委托,小说企划\nfunction handlerFollowLatest() {\n    return () => {\n        let res = JSON.parse(result)\n        return util.formatNovels(util.handNovels(util.combineNovels(res.body.thumbnails.novel)))\n    }\n}\n\n\/\/推荐小说,最近小说\nfunction handlerDiscovery() {\n    return () => {\n        let res = JSON.parse(result)\n        return util.formatNovels(util.handNovels(util.combineNovels(res.body.novels)))\n    }\n}\n\n\/\/ 搜索标签\nfunction handlerSearch() {\n    return () => {\n        let res = JSON.parse(result)\n        return util.formatNovels(util.handNovels(util.combineNovels(res.body.novel.data)))\n    }\n}\n\n\/\/ 追更列表,热门分类\nfunction handlerWatchList() {\n    return () => {\n        let res = JSON.parse(result)\n        return util.formatNovels(util.handNovels(res.body.thumbnails.novelSeries))\n    }\n}\n\n\/\/ 排行榜,书签,首页,编辑部推荐,顺序相同\nfunction handlerRanking() {\n    if (util.environment.IS_LEGADO) return handlerRankingAjaxAll()\n    \/\/ else if (util.environment.IS_SOURCE_READ) return handlerRankingWebview()\n    else if (util.environment.IS_SOURCE_READ) return handlerRankingAjax()\n    else return []\n}\n\n\/\/ 排行榜,书签,首页,编辑部推荐,顺序相同\nfunction handlerRankingAjaxAll() {\n    return () => {\n        let  novelIds = [], novelUrls = []\n        \/\/ let result = result + java.ajax(`${baseUrl}&p=2`)  \/\/ 正则获取网址中的 novelId\n        let matched = result.match(RegExp(\/\\\/novel\\\/show\\.php\\?id=\\d{5,}\/gm))\n        for (let i in matched) {\n            let novelId = matched[i].match(RegExp(\/\\d{5,}\/))[0]\n            if (novelIds.indexOf(novelId) === -1) {\n                novelIds.push(novelId)\n                novelUrls.push(urlNovelDetailed(novelId))\n            }\n        }\n        \/\/ java.log(JSON.stringify(novelIds))\n        let novels = getAjaxAllJson(novelUrls).map(resp => resp.body)\n        return util.formatNovels(util.handNovels(util.combineNovels(novels)))\n    }\n}\n\n\/\/ 排行榜,书签,首页\nfunction handlerRankingWebview() {\n    return () => {\n        let novelIds = []  \/\/ 正则获取网址中的 novelId\n        \/\/ let result = result + java.ajax(`${baseUrl}&p=2`)  \/\/ 正则获取网址中的 novelId\n        let matched = result.match(RegExp(\/\\\/novel\\\/show\\.php\\?id=\\d{5,}\/gm))\n        for (let i in matched) {\n            let novelId = matched[i].match(RegExp(\/\\d{5,}\/))[0]\n            if (novelIds.indexOf(novelId) === -1) {\n                novelIds.push(novelId)\n            }\n        }\n        \/\/ java.log(JSON.stringify(novelIds))\n        let userNovels = getWebviewJson(\n            urlNovelsDetailed(`${cache.get(\"pixiv:uid\")}`, novelIds), html => {\n                return (html.match(new RegExp(\">\\\\{.*?}<\"))[0].replace(\">\", \"\").replace(\"<\", \"\"))\n            }).body\n        return util.formatNovels(util.handNovels(util.combineNovels(Object.values(userNovels))))\n    }\n}\n\n\/\/ 排行榜,书签,顺序相同\nfunction handlerRankingAjax() {\n    return () => {\n        let novels = [], novelIds = []\n        \/\/ let result = result + java.ajax(`${baseUrl}&p=2`)  \/\/ 正则获取网址中的 novelId\n        let matched = result.match(RegExp(\/\\\/novel\\\/show\\.php\\?id=\\d{5,}\/gm))\n        for (let i in matched) {\n            let novelId = matched[i].match(RegExp(\/\\d{5,}\/))[0]\n            if (novelIds.indexOf(novelId) === -1) {\n                novelIds.push(novelId)\n                \/\/ java.log(urlNovelDetailed(novelId))\n                let res = getAjaxJson(urlNovelDetailed(novelId))\n                if (res.error !== true) {\n                    novels.push(res.body)\n                } else {\n                    java.log(JSON.stringify(res))\n                }\n            }\n        }\n        return util.formatNovels(util.handNovels(util.combineNovels(novels)))\n    }\n}\n\n(() => {\n    return handlerFactory()()\n})()",
        "bookUrl": "detailedUrl",
        "coverUrl": "coverUrl",
        "intro": "description",
        "kind": "tags",
        "lastChapter": "latestChapter",
        "name": "title",
        "wordCount": "textCount"
    },
    "ruleSearch": {
        "author": "userName",
        "bookList": "@js:\nvar util = objParse(String(java.get(\"util\")))\n\nfunction objParse(obj) {\n    return JSON.parse(obj, (n, v) => {\n        if (typeof v == \"string\" && v.match(\"()\")) {\n            return eval(`(${v})`)\n        }\n        return v;\n    })\n}\n\nvar first = true;\n\/\/ 存储seriesID\nvar seriesSet = {\n    keywords: \"Pixiv:Search\",\n    has: (value) => {\n        let page = Number(java.get(\"page\"))\n        if (page === 1 && first) {\n            first = false\n            cache.deleteMemory(this.keywords)\n            return false\n        }\n\n        let v = cache.getFromMemory(this.keywords)\n        if (v === undefined || v === null) {\n            return false\n        }\n        let set = new Set(JSON.parse(v))\n        return set.has(value)\n    },\n\n    add: (value) => {\n        let v = cache.getFromMemory(this.keywords)\n        if (v === undefined || v === null) {\n            cache.putMemory(this.keywords, JSON.stringify([value]))\n\n        } else {\n            let arr = JSON.parse(v)\n            if (typeof arr === \"string\") {\n                arr = Array(arr)\n            }\n            arr.push(value)\n            cache.putMemory(this.keywords, JSON.stringify(arr))\n        }\n    },\n};\n\nfunction getUserNovels() {\n    if (!isLogin()) {\n        sleepToast(\"👤 搜索作者\\n\\n⚠️ 当前未登录账号\\n请登录 Pixiv 账号\", 1.5)\n        util.removeCookie(); util.login()\n        sleepToast(\"👤 搜索作者\\n\\n登录成功后,请重新搜索\", 2)\n        return []\n    }\n\n    let uidList = [], novels = []\n    let username = String(java.get(\"keyword\"))\n    let page = Number(java.get(\"page\"))\n\n    \/\/ cache.delete(username)\n    let userid = cache.get(username)\n    if (userid !== undefined && userid !== null) {\n        uidList = [userid]\n        java.log(`👤 缓存作者ID:${userid}`)\n    } else {\n        html = java.ajax(urlSearchUser(username))\n        \/\/ java.log(html)\n        \/\/ 仅匹配有投稿作品的用户\n        let match = html.match(new RegExp(`\"userIds\":\\\\[(?:(?:\\\\d+,?)+)]`))\n        \/\/ java.log(JSON.stringify(match))\n        if (match === null || match.length === 0) {\n            return []\n        }\n\n        match = JSON.stringify(match).replace(\"\\\\\",\"\").split(\",\")\n        \/\/ java.log(JSON.stringify(match))\n        let regNumber = new RegExp(\"\\\\d+\")\n        uidList = match.map(v => {\n            return v.match(regNumber)[0]\n        })\n        java.log(`👤 获取作者ID:${JSON.stringify(uidList)}`)\n    }\n\n    let tempUids = []\n    for (let i in uidList) {\n        let uid = uidList[i]\n        let resp = getAjaxJson(urlIP(urlUserAllWorks(uid)), true)\n        \/\/ java.log(urlIP(urlIP(urlUserAllWorks(id))))\n        \/\/ java.log(JSON.stringify(resp))\n        if (resp.error === true) {\n            return []\n        }\n\n        \/\/ 仅获取前3个有小说的作者\n        let novelIds = Object.keys(resp.body.novels)\n        \/\/ java.log(`${uid}-${novelIds.length}`)\n        if (novelIds.length >= 1) tempUids.push(uid)\n        if (tempUids.length === 3) {\n            java.log(`👤 显示作者ID:${JSON.stringify(tempUids)}`)\n            break\n        }\n\n        \/\/ 获取系列小说,与 util.handnovels 系列详情兼容\n        let seriesIds = []\n        if (resp.body.novelSeries.length >= 1) {\n            resp.body.novelSeries.forEach(novel =>{\n                seriesIds.push(novel.id)\n                novel.textCount = novel.publishedTotalCharacterCount\n                novel.description = novel.caption\n            })\n            novels = novels.concat(resp.body.novelSeries)\n        }\n\n        \/\/ 获取所有系列内部的小说 ID\n        let seriesNovelIds = []\n        seriesIds.forEach(seriesId => {\n            let returnList = getAjaxJson(urlIP(urlSeriesNovelsTitles(seriesId))).body\n            returnList.map(novel => {return seriesNovelIds.push(novel.id)})\n        })\n        \/\/ java.log(`有系列的小说ID:${JSON.stringify(seriesNovelIds)}`)\n        \/\/ java.log(JSON.stringify(seriesNovelIds.length))\n\n        \/\/ 获取单篇小说\n        if (novelIds.length >= 1 && util.environment.IS_LEGADO) {\n            novelIds = novelIds.filter(novelid => (!seriesNovelIds.includes(novelid)))\n            novelIds = novelIds.reverse().slice((page - 1) * 20, page * 20)\n            \/\/ java.log(`真单篇的小说ID:${JSON.stringify(novelIds)}`)\n            \/\/ java.log(JSON.stringify(novelIds.length))\n            let novelUrls = novelIds.map(novelId => {return urlNovelDetailed(novelId)})\n            \/\/ java.log(JSON.stringify(novelUrls))\n            \/\/ cache.delete(novelUrls)\n            novels = novels.concat(getAjaxAllJson(novelUrls).map(resp => resp.body))\n        }\n\n        \/\/ \/\/ 获取单篇小说\n        if (novelIds.length >= 1 && util.environment.IS_SOURCE_READ) {\n            novelIds = novelIds.filter(novelid => (!seriesNovelIds.includes(novelid)))\n            \/\/ java.log(`真单篇的小说ID:${JSON.stringify(novelIds)}`)\n            \/\/ java.log(JSON.stringify(novelIds.length))\n            novelIds = novelIds.reverse().slice((page - 1) * 20, page * 20)\n            novelIds.forEach(novelId => {\n                \/\/ java.log(urlIP(urlNovelDetailed(novelId)))\n                let res = getAjaxJson(urlIP(urlNovelDetailed(novelId)))\n                if (res.error !== true) {\n                    novels.push(res.body)\n                } else {\n                    java.log(JSON.stringify(res))\n                }\n            })\n        }\n    }\n    \n    util.debugFunc(() => {\n        java.log(`获取用户搜索小说结束`)\n    })\n    return novels\n}\n\nfunction search(name, type, page) {\n    let resp = {}\n    if (type.includes(\"novel\")) {\n        resp = getAjaxJson(urlIP(urlSearchNovel(name, page)))\n        java.log(urlIP(urlSearchNovel(name, page)))\n    }\n    if (type.includes(\"series\")) {\n        resp = getAjaxJson(urlIP(urlSearchSeries(name, page)))\n        java.log(urlIP(urlSearchSeries(name, page)))\n    }\n    if (resp.error === true || resp.total === 0) {\n        return {\"data\": [], \"total\":0, \"lastPage\": 0}\n    }\n    return resp.body.novel\n}\n\nfunction getSeries() {\n    let novels = []\n    let name = String(java.get(\"keyword\"))\n    let maxPages = getFromCache(\"maxPages\")  \/\/ 仅默认搜索使用\n    if (!maxPages) {\n        maxPages = getFromCache(\"seriesMaxPages\")  \/\/ 搜索标签使用\n        if (!maxPages) maxPages = 1\n        putInCache(\"seriesMaxPages\", maxPages)\n    }\n    java.log(`📄 搜索系列最大页码:${maxPages}`)\n\n    if (JSON.parse(result).error === true) {\n        return []\n    }\n    let lastPage = JSON.parse(result).body.novel.lastPage\n    novels = novels.concat(JSON.parse(result).body.novel.data)\n    java.log(urlIP(urlSearchSeries(name, 1)))\n    cache.put(urlIP(urlSearchSeries(name, 1)), result, cacheSaveSeconds)  \/\/ 加入缓存\n    for (let page = Number(java.get(\"page\")) + 1; page <= lastPage && page <= maxPages; page++) {\n        novels = novels.concat(search(name,\"series\", page).data)\n    }\n    return novels\n}\n\nfunction getNovels() {\n    let novels = []\n    let name = String(java.get(\"keyword\"))\n    let maxPages = getFromCache(\"maxPages\")  \/\/ 仅默认搜索使用\n    if (!maxPages) {\n        maxPages = getFromCache(\"novelsMaxPages\")  \/\/ 搜索标签使用\n        if (!maxPages) maxPages = 1\n        putInCache(\"novelsMaxPages\", maxPages)\n    }\n    java.log(`📄 搜索单篇最大页码:${maxPages}`)\n\n    let resp = search(name, \"novel\", 1)\n    novels = novels.concat(resp.data)\n    for (let page = Number(java.get(\"page\")) + 1; page <= resp.lastPage && page <= maxPages; page++) {\n        novels = novels.concat(search(name,\"novel\", page).data)\n    }\n    return util.combineNovels(novels)\n}\n\nfunction getConvertNovels() {\n    let novels = []\n    let novelName = String(java.get(\"keyword\"))\n    let name1 = String(java.s2t(novelName))\n    let name2 = String(java.t2s(novelName))\n    if (name1 !== novelName) novels = novels.concat(search(name1, \"novel\", 1).data)\n    if (name2 !== novelName) novels = novels.concat(search(name2, \"novel\", 1).data)\n    novels = util.combineNovels(novels)\n    if (name1 !== novelName) novels = novels.concat(search(name1, \"series\", 1).data)\n    if (name2 !== novelName) novels = novels.concat(search(name2, \"series\", 1).data)\n    return novels\n}\n\nfunction novelFilter(novels) {\n    let textCount = 0, tags = []\n    let limitedTextCount = String(java.get(\"limitedTextCount\")).replace(\"字数\", \"\").replace(\"字數\", \"\")\n    \/\/ limitedTextCount = `3w 3k 3w5 3k5`.[0]\n    if (limitedTextCount.includes(\"w\") || limitedTextCount.includes(\"W\")) {\n        let num = limitedTextCount.toLowerCase().split(\"w\")\n        textCount = 10000 * num[0] + 1000 * num[1]\n    } else if (limitedTextCount.includes(\"k\") || limitedTextCount.includes(\"K\")) {\n        let num = limitedTextCount.toLowerCase().split(\"k\")\n        textCount = 1000 * num[0] + 100 * num[1]\n    }\n\n    let novels0 = novels.map(novel => novel.id)\n    if (textCount >= 1) {\n        novels = novels.filter(novel => novel.textCount >= textCount)\n        let novels1 = novels.map(novel => novel.id)\n        java.log(`🔢 字数限制:${limitedTextCount}`)\n        java.log(`⏬ 字数限制:过滤前${novels0.length};过滤后${novels1.length}`)\n    }\n\n    let inputTags = String(java.get(\"inputTags\")).split(\" \")\n    for (let i in inputTags) {\n        let tag = inputTags[i].trim()\n        if (tag !== \"\") tags.push(`${tag}`)\n    }\n\n    if (tags.length >= 1) {\n        \/\/ 仅保留含有所有标签的小说\n        \/\/ novels = novels.filter(novel => {\n        \/\/     \/\/ java.log(`${JSON.stringify(novel.tags)}\\n${tags.every(item => novel.tags.includes(item))}`)\n        \/\/     return tags.every(item => novel.tags.includes(item))\n        \/\/ })\n        novels = novels.filter(novel => tags.every(item => novel.tags.includes(item)))\n        let novels2 = novels.map(novel => novel.id)\n        java.log(`#️⃣ 过滤标签:${tags.join(\"、\")}`)\n        java.log(`#️⃣ 过滤标签:过滤前${novels0.length};过滤后${novels2.length}`)\n    }\n\n    let inputAuthor = String(java.get(\"inputAuthor\")).trim()\n    if (inputAuthor) {\n        \/\/ novels = novels.filter(novel => {\n        \/\/     java.log(`${novel.userName}-${novel.userName.includes(inputAuthor)}`)\n        \/\/     return novel.userName.includes(inputAuthor)\n        \/\/ })\n        novels = novels.filter(novel => novel.userName.includes(inputAuthor))\n        let novels2 = novels.map(novel => novel.id)\n        java.log(`👤 过滤作者:${tags.join(\"、\")}`)\n        java.log(`👤 过滤作者:过滤前${novels0.length};过滤后${novels2.length}`)\n    }\n    return novels\n}\n\n(() => {\n    let novels = []\n    let keyword = String(java.get(\"keyword\"))\n    if (keyword.startsWith(\"@\") || keyword.startsWith(\"@\")) {\n        java.put(\"keyword\", keyword.slice(1))\n        novels = novels.concat(getUserNovels())\n    } else if (keyword.startsWith(\"#\") || keyword.startsWith(\"#\")) {\n        java.put(\"keyword\", keyword.slice(1))\n        \/\/ 删除默认搜索最大页码,使用内部设定的最大页码\n        cache.delete(\"maxPages\")\n        novels = novels.concat(getSeries())\n        novels = novels.concat(getNovels())\n    } else {\n        \/\/ 设置默认搜索最大页码\n        putInCache(\"maxPages\", 1)\n        novels = novels.concat(getSeries())\n        novels = novels.concat(getNovels())\n        if (util.settings.SEARCH_AUTHOR) novels = novels.concat(getUserNovels())\n        if (util.settings.CONVERT_CHINESE) novels = novels.concat(getConvertNovels())\n    }\n    \/\/ java.log(JSON.stringify(novels))\n    \/\/ 返回空列表中止流程\n    if (novels.length === 0) {\n        return []\n    }\n    return novelFilter(util.formatNovels(util.handNovels(novels)))\n})()",
        "bookUrl": "detailedUrl",
        "checkKeyWord": "测试页面",
        "coverUrl": "coverUrl",
        "intro": "description",
        "kind": "tags",
        "lastChapter": "latestChapter",
        "name": "title",
        "wordCount": "textCount"
    },
    "ruleToc": {
        "chapterList": "@js:\nvar util = objParse(String(java.get(\"util\")))\n\nfunction objParse(obj) {\n    return JSON.parse(obj, (n, v) => {\n        if (typeof v == \"string\" && v.match(\"()\")) {\n            return eval(`(${v})`)\n        }\n        return v;\n    })\n}\n\nfunction urlNovel(novelId){\n    if (util.settings.SHOW_ORIGINAL_LINK) {\n        return urlNovelUrl(novelId)\n    } else {\n        return urlNovelDetailed(novelId)\n    }\n}\n\nfunction oneShotHandler(res) {\n    res.textCount = res.userNovels[`${res.id}`].textCount\n    res.createDate = timeTextFormat(res.createDate)\n    return [{\n        title: res.title.replace(RegExp(\/^\\s+|\\s+$\/g), \"\"),\n        chapterUrl: urlIP(urlNovel(res.id)),\n        chapterInfo: `${res.createDate}  ${res.textCount}字`\n    }]\n}\n\nfunction seriesHandler(res) {\n    const limit = 30\n    let returnList = [], novelIds = []\n    let seriesID = res.id, allChaptersCount = res.total\n    util.debugFunc(() => {\n        java.log(`本系列 ${seriesID} 一共有${allChaptersCount}章`);\n    })\n\n    \/\/发送请求获得相应数量的目录列表\n    function sendAjaxForGetChapters(lastIndex) {\n        resp = getAjaxJson(urlIP(urlSeriesNovels(seriesID, limit, lastIndex)), true)\n        res = resp.body.thumbnails.novel\n        \/\/ res = resp.body.page.seriesContents\n        res.forEach(v => {\n            v.title = v.title.replace(RegExp(\/^\\s+|\\s+$\/g), \"\").replace(RegExp(\/(|)|-\/g), \"\")\n            v.chapterUrl = urlIP(urlNovel(v.id))\n            novelIds.push(v.id)\n            if (v.updateDate !== undefined) {\n                v.updateDate = timeTextFormat(v.createDate)\n                v.chapterInfo = `${v.updateDate}  ${v.textCount}字`\n            } else {\n                v.updateDate = java.timeFormat(v.uploadTimestamp)\n                v.chapterInfo = `${v.updateDate}  ${v.textLength}字`\n            }\n            util.debugFunc(() => {\n                java.log(`${v.title}`)\n            })\n        })\n        return res;\n    }\n\n    if (!util.settings.SHOW_UPDATE_TIME) {\n        returnList = getAjaxJson(urlIP(urlSeriesNovelsTitles(seriesID)), true).body\n        returnList.forEach(v => {\n            v.title = v.title.replace(RegExp(\/^\\s+|\\s+$\/g), \"\").replace(RegExp(\/(|)|-\/g), \"\")\n            v.chapterUrl = urlIP(urlNovel(v.id))\n            novelIds.push(v.id)\n        })\n    } else {\n        \/\/逻辑控制者 也就是使用上面定义的两个函数来做对应功能\n        \/\/要爬取的总次数\n        let max = (allChaptersCount \/ limit) + 1\n        for (let i = 0; i < max; i++) {\n            \/\/java.log(\"i的值:\"+i)\n            let list = sendAjaxForGetChapters(i * limit);\n            \/\/取出每个值\n            returnList = returnList.concat(list)\n        }\n    }\n    \/\/ 放入小说信息以便登陆界面使用\n    let novel = source.getLoginInfoMap()\n    if (novel === undefined) novel = JSON.parse(cache.get(\"novel\"))\n    novel.novelIds = novelIds\n    cache.put(`novelIds${seriesID}`, JSON.stringify(novelIds), cacheSaveSeconds)\n    \/\/ java.log(JSON.stringify(returnList))\n    source.putLoginInfo(JSON.stringify(novel))\n    cache.put(\"novel\", JSON.stringify(novel))\n    return returnList\n}\n\n(function (res) {\n    res = util.getNovelResSeries(result)\n    if (res.firstNovelId === undefined || res.seriesNavData === null) {\n        return oneShotHandler(res)\n    } else {\n        return seriesHandler(res)\n    }\n})()",
        "chapterName": "title",
        "chapterUrl": "chapterUrl",
        "isPay": "",
        "isVip": "",
        "updateTime": "chapterInfo"
    },
    "searchUrl": "@js:\njava.put(\"key\", key)\njava.put(\"page\", page)\nlet keyword = key.split(\" \")\nlet limitedTextCount\nif (key.includes(\"字数\") || key.includes(\"字數\") ) {\n    limitedTextCount = keyword.pop()\n    keyword = keyword.join(\" \")\n} else {\n    limitedTextCount = \"\"\n    keyword = key\n}\njava.put(\"keyword\", keyword)\njava.put(\"limitedTextCount\", limitedTextCount)\n\nif (keyword.startsWith(\"@\") || keyword.startsWith(\"@\")) {\n    if (keyword.includes(\"#\") || keyword.includes(\"#\")) {\n        let author = keyword.split(\" \")[0]\n        let tags = keyword.replace(author, \"\").trim().slice(1)\n        java.put(\"keyword\", author)\n        java.put(\"inputTags\", tags)\n        java.log(`👤 搜索作者:${author} #️⃣ 过滤标签:${tags.replace(\" \", \"、\")}`)\n    } else {\n        java.put(\"keyword\", keyword)\n        java.log(`👤 搜索作者:${keyword.slice(1)}`)\n    }\n\n} else if (keyword.startsWith(\"#\") || keyword.startsWith(\"#\")) {\n    keyword = keyword.slice(1)\n    if (keyword.includes(\"@\") || keyword.includes(\"@\")) {\n        let author = keyword.match(new RegExp(\/[@@](.*)\/))\n        keyword = keyword.replace(author[0], \"\").trim()\n        java.put(\"inputAuthor\", author[1])\n        java.log(`#️⃣ 搜索标签:${keyword} 👤 过滤作者:${author[1]}`)\n    } else {\n        java.log(`#️⃣ 搜索标签:${keyword}`)\n    }\n    java.put(\"keyword\", `#${keyword}`)\n\n} else {\n    java.log(`🔍 搜索内容:${keyword}`)\n}\nurlIP(urlSearchSeries(keyword, page))",
    "variableComment": "🚫 屏蔽作者(本地):\n▶️设置方法:打开小说 - 菜单 - 登录 - 🚫 屏蔽作者\n\n🚫 屏蔽标签\/描述(本地):\n1️⃣编辑书源:菜单 - 登录 - 点击【 👀 查看屏蔽】\n2️⃣切换列表:点击按钮,切换至【相应屏蔽列表】\n3️⃣输入内容:在【输入内容】处输入屏蔽内容\n4️⃣屏蔽作者:点击【🚫 加入屏蔽】,屏蔽该内容\n\n📌 喜欢标签(本地):\n1️⃣编辑书源:菜单 - 登录 ,找到输入内容\n2️⃣输入内容:输入标签,点击【📌 喜欢标签】\n\n❤️ 查看他人收藏:\n1️⃣编辑书源:菜单 - 登录 ,找到输入内容\n2️⃣输入内容:输入作者ID,点击【❤️ 他人收藏】\n\n\n⚙️ 书源设置:\n设置1️⃣:打开小说 - 菜单 - 登录 - 点击下方按钮\n▶️ 搜索任意小说,同步设置数据\n\n设置2️⃣:编辑书源 - 基本 - 变量说明 - 修改并保存\n⚙️ 自定义设置:将 true 改为 false,或相反\n⚠️ 设置源变量【无法】更改书源自定义设置\n⚠️ 注意不要添加或删除尾随逗号\",\"\n⚠️ 更新发现页需要长按\"Pixiv\",手动刷新\n以下内容为书源设置:\n{\n\"SHOW_GENERAL_NEW\": false,\n\"SHOW_GENERAL_RANK\": false,\n\"SHOW_R18_GENRE\": false,\n\"SHOW_GENERAL_GENRE\": false\n}\n\n\/\/ SHOW_GENERAL_NEW\n\/\/ 发现:最新、企划、约稿显示一般小说\n\/\/ SHOW_GENERAL_RANK\n\/\/ 发现:排行榜显示一般小说\n\/\/ SHOW_R18_GENRE\n\/\/ 发现:热门分类显示R18小说\n\/\/ SHOW_GENERAL_GENRE\n\/\/ 发现:热门分类显示一般小说\n\n",
    "weight": 0
}
广告