/** @type {import('./_venera_.js')} */ class Baihehui extends ComicSource { // Note: The fields which are marked as [Optional] should be removed if not used // name of the source name = "百合会" // unique id of the source key = "baihehui" version = "1.0.0" minAppVersion = "1.4.0" // update url url = "https://git.nyne.dev/nyne/venera-configs/raw/branch/main/baihehui.js" settings = { domains: { title: "主页源", type: "select", options: [ { value: "yamibo.com" }, ], default: "yamibo.com" }, } get baseUrl() { return `https://www.${this.loadSetting('domains')}`; } /** * [Optional] init function */ init() { } account = { login: async (username, password) => { Network.deleteCookies("https://www.yamibo.com"); // 1. GET 登录页,保存 PHPSESSID 和 _csrf-frontend let resGet = await Network.get("https://www.yamibo.com/user/login", { headers: { "User-Agent": "Mozilla/5.0" } }); if (resGet.status !== 200) throw "无法打开登录页"; // 1.1 提取并保存 GET 返回的 Set-Cookie let sc1 = resGet.headers["set-cookie"] || resGet.headers["Set-Cookie"] || []; let initialCookies = []; for (let line of Array.isArray(sc1) ? sc1 : [sc1]) { let [pair] = line.split(";"); let [name, value] = pair.split("="); name = name.trim(); value = value.trim(); if (name === "PHPSESSID" || name === "_csrf-frontend") { initialCookies.push(new Cookie({ name, value, domain: "www.yamibo.com" })); } } Network.setCookies("https://www.yamibo.com", initialCookies); // 2. 解析 CSRF token let doc = new HtmlDocument(resGet.body); let csrf = doc .querySelector('meta[name="csrf-token"]') .attributes.content; doc.dispose(); // 3. 构造编码后的表单 let form = [ `_csrf-frontend=${encodeURIComponent(csrf)}`, `LoginForm%5Busername%5D=${encodeURIComponent(username)}`, `LoginForm%5Bpassword%5D=${encodeURIComponent(password)}`, 'LoginForm%5BrememberMe%5D=0', 'LoginForm%5BrememberMe%5D=1', `login-button=${encodeURIComponent("登录")}` ].join("&"); // 4. POST 登录(会自动带上刚才的 Cookie) let resPost = await Network.post( "https://www.yamibo.com/user/login", { "Content-Type": "application/x-www-form-urlencoded", "Referer": "https://www.yamibo.com/user/login", "User-Agent": "Mozilla/5.0" }, form ); if (resPost.status === 400) throw "登录失败"; Network.deleteCookies("https://www.yamibo.com"); // …account.login 中 POST 后提取 Cookie 部分… let raw = resPost.headers["set-cookie"] || resPost.headers["Set-Cookie"]; if (!raw) throw "未收到任何 Cookie"; // 1. 将单条字符串按“逗号+Cookie名=”拆分 let parts = Array.isArray(raw) ? raw : raw.split(/,(?=\s*(?:PHPSESSID|_identity-frontend|_csrf-frontend)=)/); // 2. 提取目标 Cookie const names = ["PHPSESSID", "_identity-frontend", "_csrf-frontend"]; let cookies = parts.map(line => { let [pair] = line.split(";"); let [k, v] = pair.split("="); k = k.trim(); v = v.trim(); if (names.includes(k)) return new Cookie({ name: k, value: v, domain: "www.yamibo.com" }); }).filter(Boolean); // 3. 验证并保存 if (cookies.length !== names.length) { throw "登录未返回完整 Cookie,实际:" + cookies.map(c => c.name).join(","); } Network.setCookies("https://www.yamibo.com", cookies); return true; }, logout: () => { Network.deleteCookies("https://www.yamibo.com"); }, registerWebsite: "https://www.yamibo.com/user/signup" } static category_types = { "全部作品": "manga/list@a@?", "原创": "manga/list?q=4@a@&", "同人": "manga/list?q=6@a@&", } static article_types = { "翻页漫画": "search/type?type=3&tag=@b@翻页漫画", "条漫": "search/type?type=3&tag=@b@条漫", "四格": "search/type?type=3&tag=@b@四格", "绘本": "search/type?type=3&tag=@b@绘本", "杂志": "search/type?type=3&tag=@b@杂志", "合志": "search/type?type=3&tag=@b@合志", } static relate_types = { "编辑推荐": "manga/rcmds?type=3012@c@&", "最近更新": "manga/latest@c@?", "原创推荐": "manga/rcmds?type=3014@c@&", "同人推荐": "manga/rcmds?type=3015@c@&", } // explore page list explore = [ { title: "百合会", type: "singlePageWithMultiPart", load: async (page) => { // 1. 拿到 HTML let res = await Network.get("https://www.yamibo.com/site/manga"); if (res.status !== 200) { throw `Invalid status code: ${res.status}`; } // 2. 解析文档 let doc = new HtmlDocument(res.body); // 3. 通用解析单元函数 function parseItem(el) { let a = el.querySelector(".media-img") || el.querySelector("a.media-img"); let href = a.attributes.href; let id = href.match(/\/manga\/(\d+)/)[1]; // 从 style 中提取 url let style = a.attributes.style || ""; let cover = `https://www.yamibo.com/coverm/000/000/${id}.jpg`; let title = el.querySelector("h3 a").text.trim(); return new Comic({ id, title, cover }); } // 4. 抓「编辑推荐」 let editor = []; let editorEls = doc.querySelectorAll(".recommend-list .media-cell.horizontal"); for (let el of editorEls) { editor.push(parseItem(el)); } // 5. 抓「最近更新」 let latest = []; // 找到标题元素,再拿其后面的