[ { "bookSourceComment": "Pixiv 小说(系列)(更新📆:2025-05-25)\n\n使用说明:两小说书源功能完全相同,启用其一即可\n可用功能:✅搜索✅发现✅添加网址✅订阅源\n搜索小说:✅单篇✅系列✅标签✅作者\n发现小说:✅关注✅追更✅推荐✅发现\n发现小说:✅收藏✅书签✅首页✅排行\n添加网址:✅Pixiv小说链接✅Pixiv系列链接\n订阅用法:点击订阅源打开小说/系列小说,【刷新】,点击【加入书架】按钮,添加到书架\n\n书源发布:兽人阅读频道 https://t.me/FurryReading\n项目地址:https://github.com/windyhusky/PixivSource\n使用教程:https://github.com/windyhusky/PixivSource/blob/main/doc/Pixiv.md\n\n规则订阅:import 订阅源\nhttps://cdn.jsdelivr.net/gh/windyhusky/PixivSource@main/import.json\nhttps://raw.githubusercontent.com/windyhusky/PixivSource/main/import.json\n\n⚙️ 书源设置:\n书源管理 - 编辑书源 - 基本 - 变量说明 - 修改并保存\n\n❤️ 查看他人收藏:\n发现 - 长按\"Pixiv\" - 编辑 - 右上角菜单 - 设置源变量\n源变量:输入作者ID,一行一个,可添加作者名,保存\n发现 - 长按\"Pixiv\" - 刷新 - 查看他人收藏", "bookSourceGroup": "🔞 Pixiv", "bookSourceName": "Pixiv 小说", "bookSourceType": 0, "bookSourceUrl": "https://www.pixiv.net/novel", "bookUrlPattern": "(https?://)?(www\\.)?pixiv\\.net(/ajax)?/novel/(show\\.php\\?id=|series/)?\\d+", "concurrentRate": "180/60000", "customOrder": 0, "enabled": true, "enabledCookieJar": false, "enabledExplore": true, "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/windyhusky/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\nbookmarks = [{\"❤️ 他人收藏 ❤️\": \"\"}]\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}\n\nsleepToast('使用说明🔖\\n\\n发现 - 更新 - 点击\"🔰 使用教程\" - 查看')\n\ntry {\n authors = String(source.getVariable()).split(\"\\n\")\n if (authors[0].trim() !== \"\" && authors.length >= 1) {\n for (let i in authors) {\n if (authors[i] !== \"\") {\n let authorId = authors[i].match(RegExp(/\\d+/))[0]\n let resp = JSON.parse(java.ajax(`https://www.pixiv.net/ajax/user/${authorId}`))\n if (resp.error !== true) {\n let bookmark = {}\n bookmark[resp.body.name] = `https://www.pixiv.net/ajax/user/${authorId}/novels/bookmarks?tag=&offset={{(page-1)*24}}&limit=24&rest=show&lang=zh`\n bookmarks.push(bookmark)\n }\n }\n }\n li = li.concat(bookmarks)\n } else {\n sleepToast('查看他人收藏❤️\\n\\n发现 - 长按\"Pixiv\" - 编辑 - 菜单 - 设置源变量')\n }\n} catch (e) {\n sleepToast(\"⚠️源变量设置有误\\n\\n输入作者ID,一行一个,可添加作者名,保存\")\n}\n\nli.forEach(item => {\n item.title = Object.keys(item)[0]\n item.url = Object.values(item)[0]\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\n\nfunction cacheGetAndSet(cache, key, supplyFunc) {\n let v = cache.get(key)\n if (v === undefined || v === null) {\n v = JSON.stringify(supplyFunc())\n cache.put(key, v, 2*60*60) // 缓存 2h\n }\n return JSON.parse(v)\n}\nfunction isJsonString(str) {\n try {\n if (typeof JSON.parse(str) === \"object\") {\n return true\n }\n } catch(e) {}\n return false\n}\nfunction getAjaxJson(url) {\n const {java, cache} = this\n return cacheGetAndSet(cache, url, () => {\n return JSON.parse(java.ajax(url))\n })\n}\nfunction getAjaxAllJson(urls) {\n const {java, cache} = this\n return cacheGetAndSet(cache, urls, () => {\n return java.ajaxAll(urls).map(resp => JSON.parse(resp.body()).body)\n })\n}\nfunction getWebviewJson(url, parseFunc) {\n const {java, cache} = this\n return cacheGetAndSet(cache, url, () => {\n let html = java.webView(null, url, null)\n return JSON.parse(parseFunc(html))\n })\n}\n\nfunction isLogin() {\n const {java} = this\n let cookie = String(java.getCookie(\"https://www.pixiv.net/\", null))\n return cookie.includes(\"first_visit_datetime\")\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 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}\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 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}\n\nfunction urlSearchNovel(novelName, page) {\n return `https://www.pixiv.net/ajax/search/novels/${novelName}?word=${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/${seriesName}?word=${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 return `${url}, {\"headers\": {\"Referer\":\"https://www.pixiv.net/\"}}`\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 = urlIllustDetailed(illustId)\n let illustOriginal = cacheGetAndSet(cache, url, () => {\n return JSON.parse(java.ajax(url))\n }).body.urls.original\n return urlCoverUrl(illustOriginal.replace(`_p0`, `_p${order - 1}`))\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 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.longToast(text)\n if (second === undefined || second <= 2) {second = 2}\n sleep(1000*second)\n}\n\nfunction updateSource(){\n return () => {\n const {java, source} = this;\n let updateUrl = \"https://cdn.jsdelivr.net/gh/windyhusky/PixivSource@main/pixiv.json\"\n let onlineSource = JSON.parse(java.get(updateUrl,{'User-Agent': 'Mozilla/5.0 (Linux; Android 14)','X-Requested-With': 'XMLHttpRequest'}).body())[0] // 第1个书源\n let comment = onlineSource.bookSourceComment.split(\"\\n\")\n\n let htm = `data:text/html; charset=utf-8,\n\n\n \n 更新 ${source.bookSourceName} 书源\n \n\n\n\n \n \n \n \n \n \n
${source.bookSourceName} 书源 🔰 使用教程
☁️ 远程仓库版本:${java.timeFormat(onlineSource.lastUpdateTime)}
📥 阅读本地版本:${java.timeFormat(source.lastUpdateTime)}
${comment.slice(2,9).join(\"
\")}
${comment.slice(comment.length-7, comment.length).join(\"
\")}
\n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n
更新 ${source.bookSourceName} 书源
\n\n`;\n java.startBrowser(htm,'更新');\n return []\n }\n}", "lastUpdateTime": 1748102400251, "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}\n\nfunction publicFunc() {\n let u = {}\n java.log(String(source.bookSourceComment).split(\"\\n\")[0]) // 输出书源信息\n java.log(`本地书源更新时间:${java.timeFormat(source.lastUpdateTime)}`) // 输出书源信息\n settings = JSON.parse(String(source.variableComment).match(RegExp(/{([\\s\\S]*?)}/gm)))\n if (settings !== null) {\n java.log(\"⚙️ 使用自定义设置\")\n } else {\n settings = {}\n settings.CONVERT_CHINESE = true // 搜索:搜索时进行繁简转换\n settings.MORE_INFORMATION = false // 详情:书籍简介显示更多信息\n settings.SHOW_UPDATE_TIME = true // 目录:显示更新时间,但会增加少许请求\n settings.SHOW_ORIGINAL_LINK = true // 目录:显示原始链接,但会增加大量请求\n settings.REPLACE_TITLE_MARKS = true // 正文:注音内容为汉字时,替换为书名号\n settings.SHOW_CAPTIONS = true // 正文:章首显示描述\n settings.SHOW_COMMENTS = true // 正文:章尾显示评论\n settings.FAST = false // 全局:快速模式\n settings.DEBUG = false // 全局:调试模式\n java.log(\"⚙️ 使用默认设置(无自定义设置 或 自定义设置有误)\")\n }\n u.CONVERT_CHINESE = settings.CONVERT_CHINESE\n u.MORE_INFORMATION = settings.MORE_INFORMATION\n u.SHOW_UPDATE_TIME = settings.SHOW_UPDATE_TIME\n u.SHOW_ORIGINAL_LINK = settings.SHOW_ORIGINAL_LINK\n u.REPLACE_TITLE_MARKS = settings.REPLACE_TITLE_MARKS\n u.SHOW_CAPTIONS = settings.SHOW_CAPTIONS\n u.SHOW_COMMENTS = settings.SHOW_COMMENTS\n u.FAST = settings.FAST\n u.DEBUG = settings.DEBUG\n\n if (u.FAST === true) {\n u.CONVERT_CHINESE = false // 搜索:繁简通搜\n u.SHOW_UPDATE_TIME = true // 目录:显示章节更新时间\n u.SHOW_ORIGINAL_LINK = false // 目录:显示章节源链接\n u.SHOW_COMMENTS = true // 正文:\n }\n if (u.DEBUG === true) {\n java.log(JSON.stringify(settings, null, 4))\n java.log(`DEBUG = ${u.DEBUG}`)\n }\n u.debugFunc = (func) => {\n if (util.DEBUG) {\n func()\n }\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 // 处理 novels 列表\n u.handNovels = function (novels, detailed=false) {\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 } else { // 搜索系列\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 }\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 !== undefined) {\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 if (novel.seriesNavData !== undefined && novel.seriesNavData !== null) {\n novel.seriesId = novel.seriesNavData.seriesId\n novel.seriesTitle = novel.seriesNavData.title\n }\n }\n // 系列详情\n if (novel.firstNovelId !== undefined) {\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 }\n\n if (novel.seriesId === undefined || novel.seriesId === null) { // 单篇\n novel.tags.unshift(\"单本\")\n novel.latestChapter = novel.title\n novel.detailedUrl = urlNovelDetailed(novel.id)\n }\n if (novel.seriesId !== undefined && detailed === false) {\n novel.id = novel.seriesId\n novel.firstNovelId = novel.novelId\n novel.title = novel.seriesTitle\n novel.tags.unshift(\"长篇\")\n novel.detailedUrl = urlSeriesDetailed(novel.seriesId)\n // novel.seriesNavData = {}\n // novel.seriesNavData.seriesId = novel.seriesId\n // novel.seriesNavData.title = novel.seriesTitle\n }\n\n if (novel.seriesId !== undefined && detailed === true) {\n let series = getAjaxJson(urlSeriesDetailed(novel.seriesId)).body\n novel.id = series.firstNovelId\n novel.title = series.title\n // novel.userName = novel.userName\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\n // 发送请求获取第一章 获取标签与简介\n let firstNovel = {}\n try {\n firstNovel = getAjaxJson(urlNovelDetailed(series.firstNovelId)).body\n novel.tags = novel.tags.concat(firstNovel.tags.tags.map(item => item.tag))\n } catch (e) { // 防止系列首篇无权限获取\n try {\n firstNovel = getAjaxJson(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 }\n novel.tags.unshift(\"长篇\")\n if (novel.description === \"\") {\n novel.description = firstNovel.description\n }\n }\n // delete novel.titleCaptionTranslation\n // delete novel.genre\n // delete novel.profileImageUrl\n // delete novel.bookmarkCount\n // delete novel.isConcluded\n // delete novel.isWatched\n // delete novel.isNotifying\n // delete novel.wordCount\n // delete novel.publishedTextLength\n // delete novel.publishedWordCount\n // delete novel.publishedReadingTime\n // delete novel.useWordCount\n // delete novel.aiType\n // delete novel.isOriginal\n // delete novel.cover\n // delete novel.titleCaptionTranslation\n })\n util.debugFunc(() => {\n java.log(`处理小说完成`)\n })\n return novels\n }\n\n // 小说信息格式化\n u.formatNovels = function (novels) {\n novels.forEach(novel => {\n novel.title = novel.title.replace(RegExp(/^\\s+|\\s+$/g), \"\")\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\n if (util.MORE_INFORMATION) {\n novel.description = `\\n书名:${novel.title}\\n作者:${novel.userName}\\n标签:${novel.tags}\\n上传:${novel.createDate}\\n更新:${novel.updateDate}\\n简介:${novel.description}`\n } else {\n novel.description = `\\n${novel.description}\\n上传时间:${novel.createDate}\\n更新时间:${novel.updateDate}`\n }\n })\n return novels\n }\n\n // 正文,详情,搜索:从网址获取id,返回单篇小说 res,系列返回首篇小说 res\n // pixiv 默认分享信息中有#号,不会被识别成链接,无法使用添加网址\n u.getNovelRes = function (result) {\n let novelId = 0, res = {\"body\": {}}\n let isJson = isJsonString(result)\n let isHtml = result.startsWith(\"\")\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 java.log(`系列ID:${id}`)\n try {\n novelId = getAjaxJson(urlSeriesDetailed(id)).body.firstNovelId\n } catch (e) {\n novelId = getAjaxJson(urlSeriesNovels(id, 30, 0)).body.thumbnails.novel[0].id\n }\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(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 = result.startsWith(\"\")\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(urlNovelDetailed(id))\n }\n }\n }\n if (isJson) {\n res = JSON.parse(result)\n }\n\n if (res.body !== undefined && res.body.seriesNavData !== undefined && res.body.seriesNavData !== null) {\n seriesId = res.body.seriesNavData.seriesId\n }\n if (seriesId) {\n java.log(`系列ID:${seriesId}`)\n res = getAjaxJson(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(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 !== undefined && new Date().getTime()- 1000*msg.modified_at <= 3*24*60*60*1000) { // 3天内进行提示\n sleepToast(`您于 ${java.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\npublicFunc()\nif (!util.FAST) checkMessageThread()\n// 获取请求的user id方便其他ajax请求构造\nlet uid = java.getResponse().headers().get(\"x-userid\")\nif (uid != null) {\n cache.put(\"pixiv:uid\", uid)\n}\njava.getStrResponse(null, null)", "loginUrl": "https://accounts.pixiv.net/login", "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], true))[0]\n if (novel.seriesId === undefined || novel.seriesId === null) {\n novel.detailedUrl = urlNovelUrl(novel.id)\n novel.catalogUrl = urlNovelDetailed(novel.id)\n } else {\n novel.detailedUrl = urlSeriesUrl(novel.seriesId)\n novel.catalogUrl = urlSeriesDetailed(novel.seriesId)\n }\n return novel\n}\n\n(() => {\n return novelHandler(util.getNovelRes(result))\n})()", "intro": "description", "kind": "tags", "lastChapter": "latestChapter", "name": "title", "tocUrl": "catalogUrl", "wordCount": "textCount" }, "ruleContent": { "content": "@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 getContent(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.SHOW_CAPTIONS === true && res.description !== \"\") {\n content = res.description + \"\\n\" + \"——————————\\n\".repeat(2) + content\n }\n\n // 获取 [uploadedimage:] 的图片链接\n let hasEmbeddedImages = res.textEmbeddedImages !== undefined && res.textEmbeddedImages !== null\n if (hasEmbeddedImages) {\n Object.keys(res.textEmbeddedImages).forEach((key) => {\n content = content.replace(`[uploadedimage:${key}]`, ``)\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 content = content.replace(`${pixivimage}`, ``)\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]}`, `${\"

\".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}

`)\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}`, `${urlName}`)\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.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 // 阅读不支持 注音\n // content = content.replace(`${matchedText}`, `${kanji}${kana}`)\n content = content.replace(`${matchedText}`, `${kanji}(${kana})`)\n }\n }\n }\n }\n\n if (util.SHOW_COMMENTS === true) {\n return content + getComment(res)\n } else {\n return content\n }\n}\n\nfunction getComment(res) {\n let comments = \"\"\n let resp = getAjaxJson(urlNovelComments(res.id, 0, 50))\n if (resp.error === true){\n return \"\"\n }\n resp.body.comments.forEach(comment =>{\n comments += `${comment.userName}:${comment.comment}\\n`\n if (comment.hasReplies === true) {\n let resp = getAjaxJson(urlNovelCommentsReply(comment.id, 1))\n if (resp.error === true) {\n return \"\"\n }\n resp.body.comments.reverse().forEach(reply =>{\n comments += `${reply.userName}(⤴️${reply.replyToUserName}):${reply.comment}\\n`\n })\n }\n })\n if (comments) {\n comments = \"\\n\" + \"——————————\\n\".repeat(2) + \"章节评论:\\n\" + 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 } 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.getNovelRes(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()\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(\"https://www.pixiv.net\")) {\n return handlerRanking()\n }\n else {\n return []\n }\n}\n\nfunction handlerNoLogin() {\n return () => {\n sleepToast(\"此功能需要在书源登录后才能使用\")\n sleepToast('发现 - 长按\"Pixiv\" - 登录 - 登录账号')\n sleepToast('订阅源处退出账号后,需要清除 Webview 数据,才能重新登录')\n sleepToast('我的 - 其他设置 - 清除 Webview 数据 - 确定 - 重新登录')\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 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 return () => {\n let novels = [], 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 // java.log(urlNovelDetailed(novelId))\n }\n }\n return util.formatNovels(util.handNovels(util.combineNovels(getAjaxAllJson(novelUrls))))\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 return []\n }\n\n let uidList = [], novels = []\n let username = String(java.get(\"keyword\"))\n let page = Number(java.get(\"page\"))\n\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\n // 仅限3个作者\n java.log(JSON.stringify(uidList))\n if (uidList.length >= 3) {\n uidList.length = 3\n }\n }\n\n uidList.forEach(id => {\n // 获取系列小说\n let resp = getAjaxJson(urlUserAllWorks(id))\n // java.log(urlUserAllWorks(id))\n if (resp.error === true) {\n return []\n }\n\n // 获取系列小说,与 util.handnovels 系列详情兼容\n resp.body.novelSeries.forEach(novel =>{\n novel.textCount = novel.publishedTotalCharacterCount\n novel.description = novel.caption\n })\n novels = novels.concat(resp.body.novelSeries)\n \n // 获取单篇小说\n let novelsId = Object.keys(resp.body.novels).reverse().slice((page - 1) * 20, page * 20)\n let url = urlNovelsDetailed(id, novelsId)\n util.debugFunc(() => {\n java.log(`发送获取作者小说的Ajax请求:${url}`)\n })\n let userNovels = getWebviewJson(url, html => {\n return (html.match(new RegExp(\">\\\\{.*?}<\"))[0].replace(\">\", \"\").replace(\"<\", \"\"))\n }).body\n // let userNovels = getAjaxJson(url).body\n // 获取对应的小说 该序列是按照id排序\n // 反转以按照更新时间排序\n novels = novels.concat(Object.values(userNovels).reverse())\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(urlSearchNovel(name, page))\n java.log(urlSearchNovel(name, page))\n }\n if (type.includes(\"series\")) {\n resp = getAjaxJson(urlSearchSeries(name, page))\n java.log(urlSearchSeries(name, page))\n }\n if (resp.error === true || resp.total === 0) {\n return {\"data\": [], \"lastPage\": 0}\n }\n return resp.body.novel\n}\n\nfunction getSeries() {\n if (JSON.parse(result).error !== true) {\n cache.put(urlSearchSeries(java.get(\"keyword\"), java.get(\"page\")), result, 30*60) // 加入缓存\n return JSON.parse(result).body.novel.data\n } else {\n return []\n }\n}\n\nfunction getNovels() {\n let MAXPAGES = 1, novels = []\n let novelName = String(java.get(\"keyword\"))\n let resp = search(novelName, \"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(novelName,\"novel\", page).data)\n }\n return 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, \"series\", 1).data)\n if (name2 !== novelName) novels = novels.concat(search(name2, \"series\", 1).data)\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 return novels\n}\n\nfunction novelFilter(novels) {\n let limitedTextCount = String(java.get(\"limitedTextCount\")).replace(\"字数\", \"\").replace(\"字數\", \"\")\n // limitedTextCount = `3w 3k 3w5 3k5`.[0]\n let textCount = 0\n if (limitedTextCount.includes(\"w\")) {\n let num = limitedTextCount.split(\"w\")\n textCount = 10000 * num[0] + 1000 * num[1]\n }\n if (limitedTextCount.includes(\"k\")) {\n let num = limitedTextCount.split(\"k\")\n textCount = 1000 * num[0] + 100 * num[1]\n }\n java.log(`字数限制:${limitedTextCount}`)\n java.log(`字数限制:${textCount}`)\n return novels.filter(novel => novel.textCount >= textCount)\n}\n\n(() => {\n let novels = []\n let keyword = String(java.get(\"keyword\"))\n if (keyword.startsWith(\"@\") || keyword.startsWith(\"@\")) {\n keyword = keyword.slice(1)\n java.log(`搜索作者:${keyword}`)\n java.put(\"keyword\", keyword)\n novels = novels.concat(getUserNovels())\n } else {\n novels = novels.concat(getNovels())\n novels = novels.concat(getSeries())\n novels = novels.concat(getUserNovels())\n if (util.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.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: urlNovel(res.id),\n chapterInfo: `${res.createDate}  ${res.textCount}字`\n }]\n}\n\nfunction seriesHandler(res) {\n const limit = 30\n let returnList = []\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(urlSeriesNovels(seriesID, limit, lastIndex))\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 = urlNovel(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.SHOW_UPDATE_TIME) {\n returnList = getAjaxJson(urlSeriesNovelsTitles(seriesID)).body\n returnList.forEach(v => {\n v.title = v.title.replace(RegExp(/^\\s+|\\s+$/g), \"\").replace(RegExp(/(|)|-/g), \"\")\n v.chapterUrl = urlNovel(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 // java.log(JSON.stringify(returnList))\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": "false", "isVip": "true", "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)\nurlSearchSeries(keyword, page)", "variableComment": "❤️ 查看他人收藏:\n发现:长按\"Pixiv\",编辑,右上角菜单,设置源变量\n设置源变量:输入作者ID,一行一个,可添加作者名,保存\n发现:长按\"Pixiv\",刷新,查看他人收藏\n以下内容为源变量模板:\n12345 作者名\n67890 //作者名\n\n\n⚙️ 自定义书源设置:\n⚙️ 自定义设置:请在基本-变量说明处修改代码\n⚙️ 自定义设置:将 true 改为 false,或相反\n⚠️ 设置源变量【无法】更改书源自定义设置\n⚠️ 注意不要添加或删除尾随逗号\",\"\n⚠️ 发现页需要长按\"Pixiv\",手动刷新\n以下内容为书源设置:\n{\n\"CONVERT_CHINESE\": true,\n\"SHOW_UPDATE_TIME\": true,\n\"MORE_INFORMATION\": false,\n\"SHOW_ORIGINAL_LINK\": true,\n\n\"REPLACE_TITLE_MARKS\": true,\n\"SHOW_CAPTIONS\": true,\n\"SHOW_COMMENTS\": true,\n\n\"FAST\": true,\n\"DEBUG\": false,\n\n\"SHOW_GENERAL_NEW\": false,\n\"SHOW_GENERAL_RANK\": false,\n\"SHOW_R18_GENRE\": false,\n\"SHOW_GENERAL_GENRE\": false\n}\n\n// CONVERT_CHINESE\n// 搜索:搜索时进行繁简转换,但搜索会变慢\n// MORE_INFORMATION\n// 详情:书籍简介显示更多信息\n\n// SHOW_UPDATE_TIME\n// 目录:显示更新时间,但会增加少许请求\n// SHOW_ORIGINAL_LINK\n// 目录:显示原始链接,但会增加大量请求\n\n// REPLACE_TITLE_MARKS\n// 正文:注音内容为汉字时,替换为书名号\n// SHOW_CAPTIONS\n// 正文:章首显示小说描述\n// SHOW_COMMENTS\n// 正文:章尾显示小说评论\n\n// FAST\n// 快速模式,禁用繁简统搜,不显示章节来源链接\n// DEBUG\n// 调试模式\n\n// SHOW_GENERAL_NEW\n// 发现:最新、企划、约稿显示一般小说\n// SHOW_GENERAL_RANK\n// 发现:排行榜显示一般小说\n// SHOW_R18_GENRE\n// 发现:热门分类显示R18小说\n// SHOW_GENERAL_GENRE\n// 发现:热门分类显示一般小说\n", "weight": 0 }, { "bookSourceComment": "Pixiv 小说(单篇)(更新📆:2025-05-25)\n\n使用说明:两小说书源功能完全相同,启用其一即可\n可用功能:✅搜索✅发现✅添加网址✅订阅源\n搜索小说:✅单篇✅系列✅标签✅作者\n发现小说:✅关注✅追更✅推荐✅发现\n发现小说:✅收藏✅书签✅首页✅排行\n添加网址:✅Pixiv小说链接✅Pixiv系列链接\n订阅用法:点击订阅源打开小说/系列小说,【刷新】,点击【加入书架】按钮,添加到书架\n\n书源发布:兽人阅读频道 https://t.me/FurryReading\n项目地址:https://github.com/windyhusky/PixivSource\n使用教程:https://github.com/windyhusky/PixivSource/blob/main/doc/Pixiv.md\n\n规则订阅:import 订阅源\nhttps://cdn.jsdelivr.net/gh/windyhusky/PixivSource@main/import.json\nhttps://raw.githubusercontent.com/windyhusky/PixivSource/main/import.json\n\n⚙️ 书源设置:\n书源管理 - 编辑书源 - 基本 - 变量说明 - 修改并保存\n\n❤️ 查看他人收藏:\n发现 - 长按\"Pixiv\" - 编辑 - 右上角菜单 - 设置源变量\n源变量:输入作者ID,一行一个,可添加作者名,保存\n发现 - 长按\"Pixiv\" - 刷新 - 查看他人收藏", "bookSourceGroup": "🔞 Pixiv", "bookSourceName": "Pixiv 单篇小说", "bookSourceType": 0, "bookSourceUrl": "https://www.pixiv.net", "bookUrlPattern": "(https?://)?(www\\.)?pixiv\\.net(/ajax)?/novel/(show\\.php\\?id=|series/)?\\d+", "concurrentRate": "180/60000", "customOrder": 1, "enabled": false, "enabledCookieJar": false, "enabledExplore": 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/windyhusky/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\nbookmarks = [{\"❤️ 他人收藏 ❤️\": \"\"}]\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}\n\nsleepToast('使用说明🔖\\n\\n发现 - 更新 - 点击\"🔰 使用教程\" - 查看')\n\ntry {\n authors = String(source.getVariable()).split(\"\\n\")\n if (authors[0].trim() !== \"\" && authors.length >= 1) {\n for (let i in authors) {\n if (authors[i] !== \"\") {\n let authorId = authors[i].match(RegExp(/\\d+/))[0]\n let resp = JSON.parse(java.ajax(`https://www.pixiv.net/ajax/user/${authorId}`))\n if (resp.error !== true) {\n let bookmark = {}\n bookmark[resp.body.name] = `https://www.pixiv.net/ajax/user/${authorId}/novels/bookmarks?tag=&offset={{(page-1)*24}}&limit=24&rest=show&lang=zh`\n bookmarks.push(bookmark)\n }\n }\n }\n li = li.concat(bookmarks)\n } else {\n sleepToast('查看他人收藏❤️\\n\\n发现 - 长按\"Pixiv\" - 编辑 - 菜单 - 设置源变量')\n }\n} catch (e) {\n sleepToast(\"⚠️源变量设置有误\\n\\n输入作者ID,一行一个,可添加作者名,保存\")\n}\n\nli.forEach(item => {\n item.title = Object.keys(item)[0]\n item.url = Object.values(item)[0]\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\n\nfunction cacheGetAndSet(cache, key, supplyFunc) {\n let v = cache.get(key)\n if (v === undefined || v === null) {\n v = JSON.stringify(supplyFunc())\n cache.put(key, v, 2*60*60) // 缓存 2h\n }\n return JSON.parse(v)\n}\nfunction isJsonString(str) {\n try {\n if (typeof JSON.parse(str) === \"object\") {\n return true\n }\n } catch(e) {}\n return false\n}\nfunction getAjaxJson(url) {\n const {java, cache} = this\n return cacheGetAndSet(cache, url, () => {\n return JSON.parse(java.ajax(url))\n })\n}\nfunction getAjaxAllJson(urls) {\n const {java, cache} = this\n return cacheGetAndSet(cache, urls, () => {\n return java.ajaxAll(urls).map(resp => JSON.parse(resp.body()).body)\n })\n}\nfunction getWebviewJson(url, parseFunc) {\n const {java, cache} = this\n return cacheGetAndSet(cache, url, () => {\n let html = java.webView(null, url, null)\n return JSON.parse(parseFunc(html))\n })\n}\n\nfunction isLogin() {\n const {java} = this\n let cookie = String(java.getCookie(\"https://www.pixiv.net/\", null))\n return cookie.includes(\"first_visit_datetime\")\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 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}\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 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}\n\nfunction urlSearchNovel(novelName, page) {\n return `https://www.pixiv.net/ajax/search/novels/${novelName}?word=${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/${seriesName}?word=${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 return `${url}, {\"headers\": {\"Referer\":\"https://www.pixiv.net/\"}}`\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 = urlIllustDetailed(illustId)\n let illustOriginal = cacheGetAndSet(cache, url, () => {\n return JSON.parse(java.ajax(url))\n }).body.urls.original\n return urlCoverUrl(illustOriginal.replace(`_p0`, `_p${order - 1}`))\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 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.longToast(text)\n if (second === undefined || second <= 2) {second = 2}\n sleep(1000*second)\n}\n\nfunction updateSource(){\n return () => {\n const {java, source} = this;\n let updateUrl = \"https://cdn.jsdelivr.net/gh/windyhusky/PixivSource@main/pixiv.json\"\n let onlineSource = JSON.parse(java.get(updateUrl,{'User-Agent': 'Mozilla/5.0 (Linux; Android 14)','X-Requested-With': 'XMLHttpRequest'}).body())[0] // 第1个书源\n let comment = onlineSource.bookSourceComment.split(\"\\n\")\n\n let htm = `data:text/html; charset=utf-8,\n\n\n \n 更新 ${source.bookSourceName} 书源\n \n\n\n\n \n \n \n \n \n \n
${source.bookSourceName} 书源 🔰 使用教程
☁️ 远程仓库版本:${java.timeFormat(onlineSource.lastUpdateTime)}
📥 阅读本地版本:${java.timeFormat(source.lastUpdateTime)}
${comment.slice(2,9).join(\"
\")}
${comment.slice(comment.length-7, comment.length).join(\"
\")}
\n \n

\n \n \n \n \n \n \n \n \n \n \n \n \n
更新 ${source.bookSourceName} 书源
\n\n`;\n java.startBrowser(htm,'更新');\n return []\n }\n}", "lastUpdateTime": 1748102400251, "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}\n\nfunction publicFunc() {\n let u = {}\n java.log(String(source.bookSourceComment).split(\"\\n\")[0]) // 输出书源信息\n java.log(`本地书源更新时间:${java.timeFormat(source.lastUpdateTime)}`) // 输出书源信息\n settings = JSON.parse(String(source.variableComment).match(RegExp(/{([\\s\\S]*?)}/gm)))\n if (settings !== null) {\n java.log(\"⚙️ 使用自定义设置\")\n } else {\n settings = {}\n settings.CONVERT_CHINESE = true // 搜索:搜索时进行繁简转换\n settings.MORE_INFORMATION = false // 详情:书籍简介显示更多信息\n settings.SHOW_UPDATE_TIME = true // 目录:显示更新时间,但会增加少许请求\n settings.SHOW_ORIGINAL_LINK = true // 目录:显示原始链接,但会增加大量请求\n settings.REPLACE_TITLE_MARKS = true // 正文:注音内容为汉字时,替换为书名号\n settings.SHOW_CAPTIONS = true // 正文:章首显示描述\n settings.SHOW_COMMENTS = true // 正文:章尾显示评论\n settings.FAST = false // 全局:快速模式\n settings.DEBUG = false // 全局:调试模式\n java.log(\"⚙️ 使用默认设置(无自定义设置 或 自定义设置有误)\")\n }\n u.CONVERT_CHINESE = settings.CONVERT_CHINESE\n u.MORE_INFORMATION = settings.MORE_INFORMATION\n u.SHOW_UPDATE_TIME = settings.SHOW_UPDATE_TIME\n u.SHOW_ORIGINAL_LINK = settings.SHOW_ORIGINAL_LINK\n u.REPLACE_TITLE_MARKS = settings.REPLACE_TITLE_MARKS\n u.SHOW_CAPTIONS = settings.SHOW_CAPTIONS\n u.SHOW_COMMENTS = settings.SHOW_COMMENTS\n u.FAST = settings.FAST\n u.DEBUG = settings.DEBUG\n\n if (u.FAST === true) {\n u.CONVERT_CHINESE = false // 搜索:繁简通搜\n u.SHOW_UPDATE_TIME = true // 目录:显示章节更新时间\n u.SHOW_ORIGINAL_LINK = false // 目录:显示章节源链接\n u.SHOW_COMMENTS = true // 正文:\n }\n if (u.DEBUG === true) {\n java.log(JSON.stringify(settings, null, 4))\n java.log(`DEBUG = ${u.DEBUG}`)\n }\n u.debugFunc = (func) => {\n if (util.DEBUG) {\n func()\n }\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 // 处理 novels 列表\n u.handNovels = function (novels, detailed=false) {\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 } else { // 搜索系列\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 }\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 !== undefined) {\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 if (novel.seriesNavData !== undefined && novel.seriesNavData !== null) {\n novel.seriesId = novel.seriesNavData.seriesId\n novel.seriesTitle = novel.seriesNavData.title\n }\n }\n // 系列详情\n if (novel.firstNovelId !== undefined) {\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 }\n\n if (novel.seriesId === undefined || novel.seriesId === null) { // 单篇\n novel.tags.unshift(\"单本\")\n novel.latestChapter = novel.title\n novel.detailedUrl = urlNovelDetailed(novel.id)\n }\n if (novel.seriesId !== undefined && detailed === false) {\n novel.id = novel.seriesId\n novel.firstNovelId = novel.novelId\n novel.title = novel.seriesTitle\n novel.tags.unshift(\"长篇\")\n novel.detailedUrl = urlSeriesDetailed(novel.seriesId)\n // novel.seriesNavData = {}\n // novel.seriesNavData.seriesId = novel.seriesId\n // novel.seriesNavData.title = novel.seriesTitle\n }\n\n if (novel.seriesId !== undefined && detailed === true) {\n let series = getAjaxJson(urlSeriesDetailed(novel.seriesId)).body\n novel.id = series.firstNovelId\n novel.title = series.title\n // novel.userName = novel.userName\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\n // 发送请求获取第一章 获取标签与简介\n let firstNovel = {}\n try {\n firstNovel = getAjaxJson(urlNovelDetailed(series.firstNovelId)).body\n novel.tags = novel.tags.concat(firstNovel.tags.tags.map(item => item.tag))\n } catch (e) { // 防止系列首篇无权限获取\n try {\n firstNovel = getAjaxJson(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 }\n novel.tags.unshift(\"长篇\")\n if (novel.description === \"\") {\n novel.description = firstNovel.description\n }\n }\n })\n util.debugFunc(() => {\n java.log(`处理小说完成`)\n })\n return novels\n }\n\n // 小说信息格式化\n u.formatNovels = function (novels) {\n novels.forEach(novel => {\n novel.title = novel.title.replace(RegExp(/^\\s+|\\s+$/g), \"\")\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\n if (util.MORE_INFORMATION) {\n novel.description = `\\n书名:${novel.title}\\n作者:${novel.userName}\\n标签:${novel.tags}\\n上传:${novel.createDate}\\n更新:${novel.updateDate}\\n简介:${novel.description}`\n } else {\n novel.description = `\\n${novel.description}\\n上传时间:${novel.createDate}\\n更新时间:${novel.updateDate}`\n }\n })\n return novels\n }\n\n // 正文,详情,搜索:从网址获取id,返回单篇小说 res,系列返回首篇小说 res\n // pixiv 默认分享信息中有#号,不会被识别成链接,无法使用添加网址\n u.getNovelRes = function (result) {\n let novelId = 0, res = {\"body\": {}}\n let isJson = isJsonString(result)\n let isHtml = result.startsWith(\"\")\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 java.log(`系列ID:${id}`)\n try {\n novelId = getAjaxJson(urlSeriesDetailed(id)).body.firstNovelId\n } catch (e) {\n novelId = getAjaxJson(urlSeriesNovels(id, 30, 0)).body.thumbnails.novel[0].id\n }\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(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 = result.startsWith(\"\")\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(urlNovelDetailed(id))\n }\n }\n }\n if (isJson) {\n res = JSON.parse(result)\n }\n\n if (res.body !== undefined && res.body.seriesNavData !== undefined && res.body.seriesNavData !== null) {\n seriesId = res.body.seriesNavData.seriesId\n }\n if (seriesId) {\n java.log(`系列ID:${seriesId}`)\n res = getAjaxJson(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(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 !== undefined && new Date().getTime()- 1000*msg.modified_at <= 3*24*60*60*1000) { // 3天内进行提示\n sleepToast(`您于 ${java.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\npublicFunc()\nif (!util.FAST) checkMessageThread()\n// 获取请求的user id方便其他ajax请求构造\nlet uid = java.getResponse().headers().get(\"x-userid\")\nif (uid != null) {\n cache.put(\"pixiv:uid\", uid)\n}\njava.getStrResponse(null, null)", "loginUrl": "https://accounts.pixiv.net/login", "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], true))[0]\n if (novel.seriesId === undefined || novel.seriesId === null) {\n novel.detailedUrl = urlNovelUrl(novel.id)\n novel.catalogUrl = urlNovelDetailed(novel.id)\n } else {\n novel.detailedUrl = urlSeriesUrl(novel.seriesId)\n novel.catalogUrl = urlSeriesDetailed(novel.seriesId)\n }\n return novel\n}\n\n(() => {\n return novelHandler(util.getNovelRes(result))\n})()", "intro": "description", "kind": "tags", "lastChapter": "latestChapter", "name": "title", "tocUrl": "catalogUrl", "wordCount": "textCount" }, "ruleContent": { "content": "@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 getContent(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.SHOW_CAPTIONS === true && res.description !== \"\") {\n content = res.description + \"\\n\" + \"——————————\\n\".repeat(2) + content\n }\n\n // 获取 [uploadedimage:] 的图片链接\n let hasEmbeddedImages = res.textEmbeddedImages !== undefined && res.textEmbeddedImages !== null\n if (hasEmbeddedImages) {\n Object.keys(res.textEmbeddedImages).forEach((key) => {\n content = content.replace(`[uploadedimage:${key}]`, ``)\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 content = content.replace(`${pixivimage}`, ``)\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]}`, `${\"

\".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}

`)\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}`, `${urlName}`)\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.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 // 阅读不支持 注音\n // content = content.replace(`${matchedText}`, `${kanji}${kana}`)\n content = content.replace(`${matchedText}`, `${kanji}(${kana})`)\n }\n }\n }\n }\n\n if (util.SHOW_COMMENTS === true) {\n return content + getComment(res)\n } else {\n return content\n }\n}\n\nfunction getComment(res) {\n let comments = \"\"\n let resp = getAjaxJson(urlNovelComments(res.id, 0, 50))\n if (resp.error === true){\n return \"\"\n }\n resp.body.comments.forEach(comment =>{\n comments += `${comment.userName}:${comment.comment}\\n`\n if (comment.hasReplies === true) {\n let resp = getAjaxJson(urlNovelCommentsReply(comment.id, 1))\n if (resp.error === true) {\n return \"\"\n }\n resp.body.comments.reverse().forEach(reply =>{\n comments += `${reply.userName}(⤴️${reply.replyToUserName}):${reply.comment}\\n`\n })\n }\n })\n if (comments) {\n comments = \"\\n\" + \"——————————\\n\".repeat(2) + \"章节评论:\\n\" + 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 } 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.getNovelRes(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()\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(\"https://www.pixiv.net\")) {\n return handlerRanking()\n }\n else {\n return []\n }\n}\n\nfunction handlerNoLogin() {\n return () => {\n sleepToast(\"此功能需要在书源登录后才能使用\")\n sleepToast('发现 - 长按\"Pixiv\" - 登录 - 登录账号')\n sleepToast('订阅源处退出账号后,需要清除 Webview 数据,才能重新登录')\n sleepToast('我的 - 其他设置 - 清除 Webview 数据 - 确定 - 重新登录')\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 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 return () => {\n let novels = [], 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 // java.log(urlNovelDetailed(novelId))\n }\n }\n return util.formatNovels(util.handNovels(util.combineNovels(getAjaxAllJson(novelUrls))))\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 return []\n }\n\n let uidList = [], novels = []\n let username = String(java.get(\"keyword\"))\n let page = Number(java.get(\"page\"))\n\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\n // 仅限3个作者\n java.log(JSON.stringify(uidList))\n if (uidList.length >= 3) {\n uidList.length = 3\n }\n }\n\n uidList.forEach(id => {\n // 获取系列小说\n let resp = getAjaxJson(urlUserAllWorks(id))\n // java.log(urlUserAllWorks(id))\n if (resp.error === true) {\n return []\n }\n\n // 获取系列小说,与 util.handnovels 系列详情兼容\n resp.body.novelSeries.forEach(novel =>{\n novel.textCount = novel.publishedTotalCharacterCount\n novel.description = novel.caption\n })\n novels = novels.concat(resp.body.novelSeries)\n\n // 获取单篇小说\n let novelsId = Object.keys(resp.body.novels).reverse().slice((page - 1) * 20, page * 20)\n let url = urlNovelsDetailed(id, novelsId)\n util.debugFunc(() => {\n java.log(`发送获取作者小说的Ajax请求:${url}`)\n })\n let userNovels = getWebviewJson(url, html => {\n return (html.match(new RegExp(\">\\\\{.*?}<\"))[0].replace(\">\", \"\").replace(\"<\", \"\"))\n }).body\n // let userNovels = getAjaxJson(url).body\n // 获取对应的小说 该序列是按照id排序\n // 反转以按照更新时间排序\n novels = novels.concat(Object.values(userNovels).reverse())\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(urlSearchNovel(name, page))\n java.log(urlSearchNovel(name, page))\n }\n if (type.includes(\"series\")) {\n resp = getAjaxJson(urlSearchSeries(name, page))\n java.log(urlSearchSeries(name, page))\n }\n if (resp.error === true || resp.total === 0) {\n return {\"data\": [], \"lastPage\": 0}\n }\n return resp.body.novel\n}\n\nfunction getSeries() {\n let MAXPAGES = 1, novels = []\n let novelName = String(java.get(\"keyword\"))\n let resp = search(novelName, \"series\", 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(novelName,\"series\", page).data)\n }\n return novels\n}\n\nfunction getNovels() {\n if (JSON.parse(result).error !== true) {\n cache.put(urlSearchNovel(java.get(\"keyword\"), java.get(\"page\")), result, 30*60) // 加入缓存\n return JSON.parse(result).body.novel.data\n } else {\n return []\n }\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, \"series\", 1).data)\n if (name2 !== novelName) novels = novels.concat(search(name2, \"series\", 1).data)\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 return novels\n}\n\nfunction novelFilter(novels) {\n let limitedTextCount = String(java.get(\"limitedTextCount\")).replace(\"字数\", \"\").replace(\"字數\", \"\")\n // limitedTextCount = `3w 3k 3w5 3k5`.[0]\n let textCount = 0\n if (limitedTextCount.includes(\"w\")) {\n let num = limitedTextCount.split(\"w\")\n textCount = 10000 * num[0] + 1000 * num[1]\n }\n if (limitedTextCount.includes(\"k\")) {\n let num = limitedTextCount.split(\"k\")\n textCount = 1000 * num[0] + 100 * num[1]\n }\n java.log(`字数限制:${limitedTextCount}`)\n java.log(`字数限制:${textCount}`)\n return novels.filter(novel => novel.textCount >= textCount)\n}\n\n(() => {\n let novels = []\n let keyword = String(java.get(\"keyword\"))\n if (keyword.startsWith(\"@\") || keyword.startsWith(\"@\")) {\n keyword = keyword.slice(1)\n java.log(`搜索作者:${keyword}`)\n java.put(\"keyword\", keyword)\n novels = novels.concat(getUserNovels())\n } else {\n novels = novels.concat(getNovels())\n novels = novels.concat(getSeries())\n novels = novels.concat(getUserNovels())\n if (util.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.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: urlNovel(res.id),\n chapterInfo: `${res.createDate}  ${res.textCount}字`\n }]\n}\n\nfunction seriesHandler(res) {\n const limit = 30\n let returnList = []\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(urlSeriesNovels(seriesID, limit, lastIndex))\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 = urlNovel(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.SHOW_UPDATE_TIME) {\n returnList = getAjaxJson(urlSeriesNovelsTitles(seriesID)).body\n returnList.forEach(v => {\n v.title = v.title.replace(RegExp(/^\\s+|\\s+$/g), \"\").replace(RegExp(/(|)|-/g), \"\")\n v.chapterUrl = urlNovel(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 // java.log(JSON.stringify(returnList))\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", "updateTime": "chapterInfo" }, "searchUrl": "@js:\njava.put(\"key\", key)\njava.put(\"page\", page)\nlet keyword = key.split(\" \")\nlet limitedTextCount\nif (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)\nurlSearchNovel(keyword, page)", "variableComment": "❤️ 查看他人收藏:\n发现:长按\"Pixiv\",编辑,右上角菜单,设置源变量\n设置源变量:输入作者ID,一行一个,可添加作者名,保存\n发现:长按\"Pixiv\",刷新,查看他人收藏\n以下内容为源变量模板:\n12345 作者名\n67890 //作者名\n\n\n⚙️ 自定义书源设置:\n⚙️ 自定义设置:请在基本-变量说明处修改代码\n⚙️ 自定义设置:将 true 改为 false,或相反\n⚠️ 设置源变量【无法】更改书源自定义设置\n⚠️ 注意不要添加或删除尾随逗号\",\"\n⚠️ 发现页需要长按\"Pixiv\",手动刷新\n以下内容为书源设置:\n{\n\"CONVERT_CHINESE\": true,\n\"SHOW_UPDATE_TIME\": true,\n\"MORE_INFORMATION\": false,\n\"SHOW_ORIGINAL_LINK\": true,\n\n\"REPLACE_TITLE_MARKS\": true,\n\"SHOW_CAPTIONS\": true,\n\"SHOW_COMMENTS\": true,\n\n\"FAST\": true,\n\"DEBUG\": false,\n\n\"SHOW_GENERAL_NEW\": false,\n\"SHOW_GENERAL_RANK\": false,\n\"SHOW_R18_GENRE\": false,\n\"SHOW_GENERAL_GENRE\": false\n}\n\n// CONVERT_CHINESE\n// 搜索:搜索时进行繁简转换,但搜索会变慢\n// MORE_INFORMATION\n// 详情:书籍简介显示更多信息\n\n// SHOW_UPDATE_TIME\n// 目录:显示更新时间,但会增加少许请求\n// SHOW_ORIGINAL_LINK\n// 目录:显示原始链接,但会增加大量请求\n\n// REPLACE_TITLE_MARKS\n// 正文:注音内容为汉字时,替换为书名号\n// SHOW_CAPTIONS\n// 正文:章首显示小说描述\n// SHOW_COMMENTS\n// 正文:章尾显示小说评论\n\n// FAST\n// 快速模式,禁用繁简统搜,不显示章节来源链接\n// DEBUG\n// 调试模式\n\n// SHOW_GENERAL_NEW\n// 发现:最新、企划、约稿显示一般小说\n// SHOW_GENERAL_RANK\n// 发现:排行榜显示一般小说\n// SHOW_R18_GENRE\n// 发现:热门分类显示R18小说\n// SHOW_GENERAL_GENRE\n// 发现:热门分类显示一般小说\n", "weight": 0 }, { "bookSourceComment": "Pixiv 漫画(更新📆:2025-05-25)\n\n可用功能:✅搜索✅发现✅添加网址✅订阅源\n搜索插画:✅漫画☑️插画❌动图✅标签\n发现漫画:✅关注✅追更✅推荐✅发现✅收藏\n添加网址:✅Pixiv插画链接✅Pixiv漫画目录\n订阅用法:点击订阅源打开插画/漫画目录,【刷新】,点击【加入书架】按钮,添加到书架\n\n书源发布:兽人阅读频道 https://t.me/FurryReading\n项目地址:https://github.com/windyhusky/PixivSource\n使用教程:https://github.com/windyhusky/PixivSource/blob/main/doc/Pixiv.md\n\n规则订阅:import 订阅源\nhttps://cdn.jsdelivr.net/gh/windyhusky/PixivSource@main/import.json\nhttps://raw.githubusercontent.com/windyhusky/PixivSource/main/import.json\n\n⚙️ 书源设置:\n书源管理 - 编辑书源 - 基本 - 变量说明 - 修改并保存", "bookSourceGroup": "🔞 Pixiv 漫画", "bookSourceName": "Pixiv 漫画", "bookSourceType": 2, "bookSourceUrl": "https://www.pixiv.net/manga", "bookUrlPattern": "(https?://)?(www\\.)?pixiv\\.net/((artworks|user/\\d+/series)|ajax/(illust|series))/\\d+", "concurrentRate": "180/60000", "customOrder": 2, "enabled": true, "enabledCookieJar": false, "enabledExplore": true, "exploreUrl": "@js:\nlet SHOW_GENERAL_NEW, SHOW_GENERAL_RANK\ntry {\n settings = JSON.parse(String(source.variableComment).match(RegExp(/{([\\s\\S]*?)}/gm)))\n SHOW_GENERAL_NEW = settings.SHOW_GENERAL_NEW // 发现:最新、企划、约稿显示一般小说\n SHOW_GENERAL_RANK = settings.SHOW_GENERAL_RANK // 发现:排行榜显示一般小说\n} catch (e) {\n SHOW_GENERAL_NEW = false\n SHOW_GENERAL_RANK = false\n}\n\nli = [\n {\"⭐️ 关注\": \"https://www.pixiv.net/ajax/follow_latest/illust?p={{page}}&mode=all&lang=zh\"},\n {\"📃 追更\": \"https://www.pixiv.net/ajax/watch_list/manga?p={{page}}&new=1&lang=zh\"},\n {\"💯 推荐\": \"https://www.pixiv.net/ajax/top/illust?mode=all&lang=zh\"},\n {\"🔍 发现\": \"https://www.pixiv.net/ajax/illust/discovery?mode=all&lang=zh\"},\n {\"❤️ 收藏\": \"https://www.pixiv.net/ajax/user/{{cache.get(\\\"pixiv:uid\\\")}}/illusts/bookmarks?tag=&offset={{(page-1)*24}}&limit=24&rest=show&lang=zh\"},\n {\"㊙️ 收藏\": \"https://www.pixiv.net/ajax/user/{{cache.get(\\\"pixiv:uid\\\")}}/illusts/bookmarks?tag=&offset={{(page-1)*24}}&limit=24&rest=hide&lang=zh\"},\n {\"🏠 首页\": \"https://www.pixiv.net\"},\n {\"🆙 更新\": \"https://cdn.jsdelivr.net/gh/windyhusky/PixivSource@main/pixiv.json\"}\n]\n\ngeneralNew = [\n {\"🆕 最新 企划 约稿 💰\": \"\"},\n {\"🆕 最新\": \"https://www.pixiv.net/ajax/illust/new?lastId=0&limit=20&type=manga&lang=zh\"},\n {\"📑 企划\": \"https://www.pixiv.net/ajax/user_event/portal/artworks?mode=all&p={{page}}&lang=zh\"},\n {\"💰 约稿\": \"https://www.pixiv.net/ajax/commission/page/request/complete/manga?mode=all&p={{page}}&lang=zh\"},\n {\"🔍 发现\": \"https://www.pixiv.net/ajax/illust/discovery?mode=all&lang=zh\"}\n]\n\nr18Rank = [\n {\"👑 排行榜单 👑\": \"\"},\n {\"今日\": \"https://www.pixiv.net/ranking.php?mode=daily_r18&content=manga&p={{page}}&format=json\"},\n {\"本周\": \"https://www.pixiv.net/ranking.php?mode=weekly_r18&content=manga&p={{page}}&format=json\"},\n {\"R18G\": \"https://www.pixiv.net/ranking.php?mode=r18g&content=manga&p={{page}}&format=json\"},\n {\"男性\": \"https://www.pixiv.net/ranking.php?mode=male_r18\"},\n {\"女性\": \"https://www.pixiv.net/ranking.php?mode=female_r18\"}\n]\n\ngeneralRank = [\n {\"🏆 排行榜单 🏆\": \"\"},\n {\"今日\": \"https://www.pixiv.net/ranking.php?mode=daily&content=manga&p={{page}}&format=json\"},\n {\"本周\": \"https://www.pixiv.net/ranking.php?mode=weekly&content=manga&p={{page}}&format=json\"},\n {\"本月\": \"https://www.pixiv.net/ranking.php?mode=monthly&content=manga&p={{page}}&format=json\"},\n {\"新人\": \"https://www.pixiv.net/ranking.php?mode=rookie&content=manga&p={{page}}&format=json\"}\n]\n\nif (SHOW_GENERAL_RANK === true) {\n li = li.concat(generalNew)\n}\nli = li.concat(r18Rank)\nif (SHOW_GENERAL_RANK === true) {\n li = li.concat(generalRank)\n}\n\nli.forEach(item => {\n item.title = Object.keys(item)[0]\n item.url = Object.values(item)[0]\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": "function cacheGetAndSet(cache, key, supplyFunc) {\n let v = cache.get(key)\n if (v === undefined || v === null) {\n v = JSON.stringify(supplyFunc())\n // 缓存10分钟\n cache.put(key, v, 600)\n }\n return JSON.parse(v)\n}\nfunction isJsonString(str) {\n try {\n if (typeof JSON.parse(str) === \"object\") {\n return true\n }\n } catch(e) {}\n return false\n}\nfunction getAjaxJson(url) {\n const {java, cache} = this\n return cacheGetAndSet(cache, url, () => {\n return JSON.parse(java.ajax(url))\n })\n}\nfunction getWebviewJson(url, parseFunc) {\n const {java, cache} = this\n return cacheGetAndSet(cache, url, () => {\n let html = java.webView(null, url, null)\n return JSON.parse(parseFunc(html))\n })\n}\n\nfunction isLogin() {\n const {java} = this\n let cookie = String(java.getCookie(\"https://www.pixiv.net/\", null))\n return cookie.includes(\"first_visit_datetime\")\n}\n\nfunction urlIllustUrl(illustId) {\n return `https://www.pixiv.net/artworks/${illustId}`\n}\nfunction urlIllustDetailed(illustId) {\n return `https://www.pixiv.net/ajax/illust/${illustId}?lang=zh`\n}\nfunction urlIllustsDetailed(userId, idList) {\n return `https://www.pixiv.net/ajax/user/${userId}/illusts?${idList.map(v => \"ids[]=\" + v).join(\"&\")}`\n}\nfunction urlSeriesUrl(userId, seriesId) {\n return `https://www.pixiv.net/user/${userId}/series/${seriesId}`\n}\nfunction urlSeriesDetailed(seriesId, page=1) {\n return `https://www.pixiv.net/ajax/series/${seriesId}?p=${page}&lang=zh`\n}\n\nfunction urlUserAllWorks(userId) {\n return `https://www.pixiv.net/ajax/user/${userId}/profile/all?lang=zh`\n}\n\nfunction urlSearchArtwork(name, page) {\n return `https://www.pixiv.net/ajax/search/artworks/${encodeURI(name)}?word=${encodeURI(name)}&order=date_d&mode=all&p=${page}&s_mode=s_tc&type=all&lang=zh`\n}\nfunction urlSearchIllust(name, page) {\n return `https://www.pixiv.net/ajax/search/artworks/${encodeURI(name)}?word=${encodeURI(name)}&order=date_d&mode=all&p=${page}&s_mode=s_tc&type=illust&lang=zh`\n}\nfunction urlSearchManga(name, page) {\n return `https://www.pixiv.net/ajax/search/artworks/${encodeURI(name)}?word=${encodeURI(name)}&order=date_d&mode=all&p=${page}&s_mode=s_tc&type=manga&lang=zh`\n}\nfunction urlSearchUgoira(name, page) {\n return `https://www.pixiv.net/ajax/search/artworks/${encodeURI(name)}?word=${encodeURI(name)}&order=date_d&mode=all&p=${page}&s_mode=s_tc&type=ugoira&lang=zh`\n}\n// 完全匹配用户名\nfunction urlSearchUser(name) {\n return `https://www.pixiv.net/search/users?nick=${encodeURI(name)}&s_mode=s_usr&nick_mf=1`\n}\n\nfunction urlCoverUrl(url) {\n return `${url}, {\"headers\": {\"Referer\":\"https://www.pixiv.net/\"}}`\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 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.longToast(text)\n if (second === undefined || second <= 2) {second = 2}\n sleep(1000*second)\n}\n\nfunction updateSource(){\n return () => {\n const {java, source} = this;\n let updateUrl = \"https://cdn.jsdelivr.net/gh/windyhusky/PixivSource@main/pixiv.json\"\n let onlineSource = JSON.parse(java.get(updateUrl,{'User-Agent': 'Mozilla/5.0 (Linux; Android 14)','X-Requested-With': 'XMLHttpRequest'}).body())[2] // 第3个书源\n let comment = onlineSource.bookSourceComment.split(\"\\n\")\n\n let htm = `data:text/html; charset=utf-8,\n\n\n \n 更新 ${source.bookSourceName} 书源\n \n\n\n\n \n \n \n \n \n \n
${source.bookSourceName} 书源 🔰 使用教程
☁️ 远程仓库版本:${java.timeFormat(onlineSource.lastUpdateTime)}
📥 阅读本地版本:${java.timeFormat(source.lastUpdateTime)}
${comment.slice(2,7).join(\"
\")}
${comment.slice(comment.length-2, comment.length).join(\"
\")}
\n \n

\n \n \n \n \n \n \n \n \n \n \n \n \n
更新 ${source.bookSourceName} 书源
\n\n`;\n java.startBrowser(htm,'更新');\n return []\n }\n}", "lastUpdateTime": 1748102400251, "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}\n\nfunction publicFunc() {\n let u = {}, settings = {}\n java.log(String(source.bookSourceComment).split(\"\\n\")[0]) // 输出书源信息\n java.log(`本地书源更新时间:${java.timeFormat(source.lastUpdateTime)}`) // 输出书源信息\n settings = JSON.parse(String(source.variableComment).match(RegExp(/{([\\s\\S]*?)}/gm)))\n if (settings !== null) {\n java.log(\"⚙️ 使用自定义设置\")\n } else {\n settings = {}\n settings.CONVERT_CHINESE = true // 搜索:搜索时进行繁简转换\n settings.SEARCH_ILLUSTS = false // 搜索插画\n settings.SHOW_ORIGINAL_LINK = true // 目录处显示源链接,但会增加请求次数\n settings.DEBUG = false // 调试模式\n java.log(\"⚙️ 使用默认设置(无自定义设置 或 自定义设置有误)\")\n }\n u.CONVERT_CHINESE = settings.CONVERT_CHINESE\n u.SEARCH_ILLUSTS = settings.SEARCH_ILLUSTS\n u.SHOW_ORIGINAL_LINK = settings.SHOW_ORIGINAL_LINK\n u.DEBUG = settings.DEBUG\n\n if (u.DEBUG === true) {\n java.log(JSON.stringify(settings, null, 4))\n java.log(`DEBUG = ${u.DEBUG}`)\n }\n u.debugFunc = (func) => {\n if (util.DEBUG) {\n func()\n }\n }\n\n u.handIllusts = function (illusts) {\n illusts.forEach(illust => {\n // illust.id = illust.id\n // illust.title = illust.title\n // illust.userName = illust.userName\n // illust.tags = illust.tags\n if (!(illust.tags instanceof Array)) {\n illust.tags = illust.tags.tags.map(item => item.tag)\n illust.coverUrl = illust.url = illust.urls.regular // 兼容正文搜索\n illust.updateDate = illust.uploadDate\n }\n illust.textCount = null\n // illust.pageCount = illust.pageCount\n // illust.description = illust.description\n illust.coverUrl = illust.url\n illust.detailedUrl = urlIllustDetailed(illust.id)\n // illust.createDate = illust.createDate\n // illust.updateDate = illust.updateDate\n // illust.aiType = illust.aiType\n\n if (illust.seriesNavData === undefined || illust.seriesNavData === null) {\n illust.latestChapter = illust.title\n } else {\n illust.seriesId = illust.seriesNavData.seriesId\n illust.title = illust.seriesNavData.title\n }\n\n if (illust.seriesId !== undefined) {\n let resp = getAjaxJson(urlSeriesDetailed(illust.seriesId)).body\n let series = resp.illustSeries.filter(item => item.id === illust.seriesId)[0]\n // illust.title = illust.title\n illust.tags = illust.tags.concat(series.tags)\n illust.latestChapter = resp.thumbnails.illust.filter(item => item.id === series.latestIllustId)[0].title\n illust.description = series.description\n if (series.url === undefined) {\n let firstChapter = getAjaxJson(urlIllustDetailed(series.firstIllustId)).body\n illust.coverUrl = firstChapter.urls.regular\n illust.tags = illust.tags.concat(firstChapter.tags.tags.map(item => item.tag))\n }\n illust.createDate = series.createDate\n illust.updateDate = series.updateDate\n illust.total = series.total\n }\n })\n return illusts\n }\n\n u.formatIllusts = function (illusts) {\n illusts.forEach(illust => {\n illust.title = illust.title.replace(RegExp(/^\\s+|\\s+$/g), \"\")\n illust.tags = Array.from(new Set(illust.tags))\n illust.tags = illust.tags.join(\",\")\n illust.coverUrl = urlCoverUrl(illust.coverUrl)\n illust.createDate = dateFormat(illust.createDate)\n illust.updateDate = dateFormat(illust.updateDate)\n if (util.MORE_INFORMATION) {\n illust.description = `\\n书名:${illust.title}\\n作者:${illust.userName}\\n标签:${illust.tags}\\n页面:${illust.pageCount}\\n上传:${illust.createDate}\\n更新:${illust.updateDate}\\n简介:${illust.description}`\n } else {\n illust.description = `\\n${illust.title},共${illust.pageCount}页\\n${illust.description}\\n上传时间:${illust.createDate}\\n更新时间:${illust.updateDate}`\n }\n })\n return illusts\n }\n\n u.getIllustRes = function (result) {\n let illustId = 0, res = {}\n let isJson = isJsonString(result)\n let isHtml = result.startsWith(\"\")\n if (!isJson && isHtml) {\n let pattern1 = \"(https?://)?(www\\\\.)?pixiv\\\\.net/(artworks|ajax/illust)/(\\\\d+)\"\n let isIllust = baseUrl.match(new RegExp(pattern1))\n let pattern2 = \"(https?://)?(www\\\\.)?pixiv\\\\.net/(user/\\\\d+|ajax)/series/(\\\\d+)\"\n let isSeries = baseUrl.match(new RegExp(pattern2))\n\n if (isIllust) {\n illustId = isIllust[4]\n } else if (isSeries) {\n seriesId = isSeries[4]\n java.log(`匹配系列ID:${seriesId}`)\n illustId = getAjaxJson(urlSeriesDetailed(seriesId)).body.page.series.reverse()[0].workId\n }\n }\n if (isJson) {\n res = JSON.parse(result)\n }\n\n if (illustId) {\n java.log(`匹配插画ID:${illustId}`)\n res = getAjaxJson(urlIllustDetailed(illustId))\n }\n if (res.error) {\n java.log(`无法从 Pixiv 获取当前漫画`)\n java.log(JSON.stringify(res))\n return []\n }\n return res.body\n }\n\n util = u\n java.put(\"util\", objStringify(u))\n}\n\npublicFunc()\n\n// 获取请求的user id方便其他ajax请求构造\nlet uid = java.getResponse().headers().get(\"x-userid\")\nif (uid != null) {\n cache.put(\"pixiv:uid\", uid)\n}\njava.getStrResponse(null, null)", "loginUrl": "https://accounts.pixiv.net/login", "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 illustHandler(illust){\n illust = util.formatIllusts(util.handIllusts([illust]))[0]\n illust.detailedUrl = urlIllustUrl(illust.id)\n illust.catalogUrl = urlIllustDetailed(illust.id)\n return illust\n}\n\n(() => {\n try {\n return illustHandler(util.getIllustRes(result))\n } catch (e) {\n java.log(e)\n java.log(`受 Pixiv 的限制,无法获取当前插画的数据`)\n }\n})()", "intro": "description", "kind": "tags", "lastChapter": "latestChapter", "name": "title", "tocUrl": "catalogUrl", "wordCount": "" }, "ruleContent": { "content": "@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 getContent(res) {\n let content = [\"\"]\n // li = \"mini thumb small regular original\".split(\" \")\n let illustLink = getAjaxJson(urlIllustDetailed(res.id)).body.urls.regular\n for (let order = 0; order < res.pageCount; order++) {\n content.push(``)\n illustLink = illustLink.replace(`_p${order}`, `_p${order + 1}`)\n }\n content = content.join(\"\\n\")\n return content\n}\n\n(function () {\n return getContent(util.getIllustRes(result))\n})()", "imageStyle": "FULL" }, "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()\n }\n if (!isLogin()) {\n return handlerNoLogin()\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(\"/top\")) {\n return handlerRecommend()\n }\n if (baseUrl.includes(\"/discovery\")) {\n return handlerDiscovery()\n }\n if (baseUrl.includes(\"/bookmark\")) {\n return handlerBookMarks()\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 // 正则匹配网址内容\n if (baseUrl.includes(\"/ranking\") && (baseUrl.endsWith(\"json\"))) {\n return handlerRanking()\n }\n if (baseUrl.includes(\"/ranking\")) {\n return handlerRegexIllusts()\n }\n else {\n return []\n }\n}\n\nfunction handlerNoLogin() {\n return () => {\n sleepToast(\"此功能需要在书源登录后才能使用\")\n sleepToast('发现 - 长按\"Pixiv\" - 登录 - 登录账号')\n return []\n }\n}\n\n//关注作者,漫画委托,漫画企划\nfunction handlerFollowLatest() {\n return () => {\n let res = JSON.parse(result)\n return util.formatIllusts(util.handIllusts(res.body.thumbnails.illust))\n }\n}\n\n// 追更列表\nfunction handlerWatchList() {\n return () => {\n let res = JSON.parse(result)\n // li = res.body.page.watchedSeriesIds\n return util.formatIllusts(util.handIllusts(res.body.thumbnails.illust))\n }\n}\n\n// 推荐漫画\nfunction handlerRecommend() {\n return () => {\n let res = JSON.parse(result)\n const recommend = res.body.page.recommend\n const illusts = res.body.thumbnails.illust\n let nidSet = new Set(recommend.ids)\n // java.log(nidSet.size)\n let list = illusts.filter(illust => nidSet.has(String(illust.id)))\n // java.log(`过滤结果:${JSON.stringify(list)}`)\n return util.formatIllusts(util.handIllusts(list))\n }\n}\n\n//发现漫画\nfunction handlerDiscovery() {\n return () => {\n let res = JSON.parse(result)\n return util.formatIllusts(util.handIllusts(res.body.illusts))\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.formatIllusts(util.handIllusts(res))\n }\n}\n\n// 排行榜,顺序相同\nfunction handlerRanking() {\n return () => {\n let res = JSON.parse(result)\n res.contents.forEach(item =>{\n item.id = item.illust_id\n // item.title = item.title\n item.userName = item.user_name\n // item.tags = item.tags\n item.latestChapter = item.title\n item.description = null\n item.coverUrl = item.url\n item.detailedUrl = urlIllustDetailed(item.id)\n item.createDate = item.updateDate = item.illust_upload_timestamp * 1000\n\n if (item.illust_series !== false) {\n let series = item.illust_series\n item.seriesId = series.illust_series_id\n item.order = series.illust_series_content_order\n item.total = series.illust_series_content_count\n if (item.order === item.total) item.latestChapter = item.title\n item.title = series.illust_series_title\n item.description = series.illust_series_caption\n item.pageCount = series.illust_page_count\n item.createDate = item.updateDate = series.illust_series_create_datetime\n }\n })\n return util.formatIllusts(util.handIllusts(res.contents))\n }\n}\n\n//首页,编辑部推荐,顺序随机\nfunction handlerRegexIllusts() {\n return () => {\n let illustIds = [] // 正则获取网址中的 illustId\n let matched = result.match(RegExp(/\\/artworks\\/\\d{5,}/gm))\n for (let i in matched) {\n let illustId = matched[i].match(RegExp(/\\d{5,}/))[0]\n if (illustIds.indexOf(illustId) === -1) {\n illustIds.push(illustId)\n }\n }\n let userIllusts = getWebviewJson(\n urlIllustsDetailed(`${cache.get(\"pixiv:uid\")}`, illustIds), html => {\n return (html.match(new RegExp(\">\\\\{.*?}<\"))[0].replace(\">\", \"\").replace(\"<\", \"\"))\n }).body\n return util.formatIllusts(util.handIllusts(Object.values(userIllusts)))\n }\n}\n\n(() => {\n return handlerFactory()()\n})()", "bookUrl": "detailedUrl", "coverUrl": "coverUrl", "intro": "description", "kind": "tags", "lastChapter": "latestChapter", "name": "title", "wordCount": "" }, "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\nfunction getManga() {\n if (JSON.parse(result).error === true) {\n return []\n }\n let illusts = JSON.parse(result).body.illustManga.data\n illusts.forEach(illust => {illust.tags.unshift(\"漫画\")})\n return illusts\n}\n\nfunction getConvertManga() {\n let MAXPAGES = 2, illusts = []\n let novelName = String(java.get(\"key\"))\n let name = java.s2t(java.t2s(java.s2t(novelName)))\n let resp = getAjaxJson(urlSearchManga(name, 1)).body\n java.log(urlSearchManga(name, 1))\n illusts = illusts.concat(resp.illustManga.data)\n // for (let page = 2; page < resp.lastPage, page < MAXPAGES; page++) {\n // illusts = illusts.concat(getAjaxJson(urlSearchManga(name, page)).body.illustManga.data)\n // java.log(urlSearchManga(name, page))\n // }\n illusts.forEach(illust => {illust.tags.unshift(\"漫画\")})\n return illusts\n}\n\nfunction getIllust() {\n let MAXPAGES = 3, illusts = []\n let name = String(java.get(\"key\"))\n java.log(urlSearchIllust(name, 1))\n let resp = getAjaxJson(urlSearchIllust(name, 1))\n if (resp.error === true) {\n return []\n }\n illusts = illusts.concat(resp.body.illustManga.data)\n for (let page = Number(java.get(\"page\")) + 1; page < resp.body.illustManga.lastPage, page < MAXPAGES; page++) {\n java.log(`正在搜索第${page}页`)\n let resp = getAjaxJson(urlSearchIllust(name, page))\n if (resp.error === true) {\n return []\n }\n illusts = illusts.concat(resp.body.illustManga.data)\n }\n illusts.forEach(illust => {illust.tags.unshift(\"插画\")})\n return illusts\n}\n\n(() => {\n let artworks = []\n artworks = artworks.concat(getManga())\n if (util.CONVERT_CHINESE) artworks = artworks.concat(getConvertManga())\n if (util.SEARCH_ILLUSTS) artworks = artworks.concat(getIllust())\n // java.log(JSON.stringify(artworks))\n // 返回空列表中止流程\n if (artworks.length === 0) {\n return []\n }\n return util.formatIllusts(util.handIllusts(artworks))\n})()", "bookUrl": "detailedUrl", "checkKeyWord": "测试", "coverUrl": "coverUrl", "intro": "description", "kind": "tags", "lastChapter": "latestChapter", "name": "title", "wordCount": "" }, "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 urlIllust(novelId){\n if (util.SHOW_ORIGINAL_LINK) {\n return urlIllustUrl(novelId)\n } else {\n return urlIllustDetailed(novelId)\n }\n}\n\nfunction oneShotHandler(res) {\n return [{\n title: res.title.replace(RegExp(/^\\s+|\\s+$/g), \"\"),\n chapterUrl: urlIllust(res.id),\n chapterInfo: `${timeTextFormat(res.createDate)}`\n }]\n}\n\nfunction seriesHandler(res) {\n let limit = 12, total = 0, illusts = []\n let seriesId = res.seriesNavData.seriesId\n if (res.seriesId === undefined) {\n total = getAjaxJson(urlSeriesDetailed(res.seriesNavData.seriesId)).body.page.total\n } else {\n total = res.total\n }\n util.debugFunc(() => {\n java.log(`本系列 ${seriesId} 一共有${total}章`);\n })\n\n //要爬取的总次数\n let max = (total / limit) + 1\n for (let page = 1; page < max; page++) {\n // java.log(urlSeriesDetailed(seriesId, page))\n res = getAjaxJson(urlSeriesDetailed(seriesId, page)).body\n let illusts_id = res.page.series.map(item => item.workId)\n illusts = illusts.concat(res.thumbnails.illust.filter(illust => illusts_id.includes(illust.id)))\n }\n illusts.reverse().forEach(illust => {\n illust.title = illust.title.replace(RegExp(/^\\s+|\\s+$/g), \"\")\n illust.chapterUrl = urlIllust(illust.id)\n illust.chapterInfo = timeTextFormat(illust.createDate)\n })\n // java.log(JSON.stringify(illusts))\n return illusts\n}\n\n(() => {\n let res = util.getIllustRes(result)\n if (res.seriesNavData !== null) {\n return seriesHandler(res)\n } else {\n return oneShotHandler(res)\n }\n})()", "chapterName": "title", "chapterUrl": "chapterUrl", "isPay": "", "isVip": "", "updateTime": "chapterInfo" }, "searchUrl": "@js:\njava.put(\"key\",key)\njava.put(\"page\",page)\nurlSearchManga(key, page)", "variableComment": "⚙️ 自定义书源设置:\n⚙️ 自定义设置:请在基本-变量说明处修改代码\n⚙️ 自定义设置:将 true 改为 false,或相反\n⚠️ 设置源变量【无法】更改书源自定义设置\n⚠️ 注意不要添加或删除尾随逗号\",\"\n⚠️ 发现页需要长按\"Pixiv\",手动刷新\n以下内容为书源设置:\n{\n\"CONVERT_CHINESE\": true,\n\"SEARCH_ILLUSTS\": false,\n\"SHOW_GENERAL_NEW\": false,\n\n\"DEBUG\": false,\n\n\"SHOW_GENERAL_RANK\": false,\n\"SHOW_ORIGINAL_LINK\": true\n}\n\n// CONVERT_CHINESE\n// 搜索:搜索时进行繁简转换\n// SEARCH_ILLUSTS\n// 搜索:同时搜索插画\n// SHOW_ORIGINAL_LINK\n// 目录:显示源链接,但会增加请求次数\n\n// DEBUG\n// 调试模式\n\n// SHOW_GENERAL_NEW\n// 发现:最新、企划、约稿显示一般漫画\n// SHOW_GENERAL_RANK\n// 发现:排行榜显示一般漫画\n\n", "weight": 0 } ]