mirror of
https://github.com/venera-app/venera-configs.git
synced 2025-09-27 00:27:23 +00:00
Update manhuagui (#136)
* add new source from comick * fix some code * fix gif load and comic list info(none-type/chapter/volume) * add some comick hidden tags * revise coding error in file * info updata time * fix no-EN error * add new function - Multi-language comic selection support - Added comic recommendations - Fixed empty chapter return bug - Resolved tag click issues - Optimized data processing * Optimize network request Remove redundant requests and prevent async deadlocks * Update comick.js * new small comic source from baihehui * Fixed some bugs and added some sorting methods * Fixed some bugs and added some sorting methods * Add a new resource from ykmh * Remove invalid request * fixed chapter api * Update index.json * Update index.json * Update comick.js * fix search bug from manhuagui Fix ”querySelectorAll“ bug in search page. Add multi-group of chapters in info page. * add lanraragi * Update manhuagui.js login, comment, favorites * Update index.json * Update index.json
This commit is contained in:
@@ -96,7 +96,7 @@
|
|||||||
"name": "漫画柜",
|
"name": "漫画柜",
|
||||||
"fileName": "manhuagui.js",
|
"fileName": "manhuagui.js",
|
||||||
"key": "ManHuaGui",
|
"key": "ManHuaGui",
|
||||||
"version": "1.0.1"
|
"version": "1.1.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "优酷漫画",
|
"name": "优酷漫画",
|
||||||
|
478
manhuagui.js
478
manhuagui.js
@@ -1,23 +1,68 @@
|
|||||||
/** @type {import('./_venera_.js')} */
|
/** @type {import('./_venera_.js')} */
|
||||||
class ManHuaGui extends ComicSource {
|
class ManHuaGui extends ComicSource {
|
||||||
// Note: The fields which are marked as [Optional] should be removed if not used
|
|
||||||
|
|
||||||
// name of the source
|
|
||||||
name = "漫画柜";
|
name = "漫画柜";
|
||||||
|
|
||||||
// unique id of the source
|
|
||||||
key = "ManHuaGui";
|
key = "ManHuaGui";
|
||||||
|
|
||||||
version = "1.0.1";
|
version = "1.1.0";
|
||||||
|
|
||||||
minAppVersion = "1.4.0";
|
minAppVersion = "1.4.0";
|
||||||
|
|
||||||
// update url
|
url = "https://git.nyne.dev/nyne/venera-configs/raw/branch/main/manhuagui.js";
|
||||||
url =
|
|
||||||
"https://git.nyne.dev/nyne/venera-configs/raw/branch/main/manhuagui.js";
|
|
||||||
|
|
||||||
baseUrl = "https://www.manhuagui.com";
|
baseUrl = "https://www.manhuagui.com";
|
||||||
|
|
||||||
|
account = {
|
||||||
|
login: async (username, password) => {
|
||||||
|
let headers = {
|
||||||
|
'content-type': 'application/x-www-form-urlencoded',
|
||||||
|
'accept': 'application/json, text/javascript, */*; q=0.01',
|
||||||
|
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
||||||
|
'cache-control': 'no-cache',
|
||||||
|
'pragma': 'no-cache',
|
||||||
|
'x-requested-with': 'XMLHttpRequest',
|
||||||
|
'origin': this.baseUrl,
|
||||||
|
'referer': `${this.baseUrl}/`,
|
||||||
|
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
|
||||||
|
};
|
||||||
|
let body = `txtUserName=${encodeURIComponent(username)}&txtPassword=${encodeURIComponent(password)}`;
|
||||||
|
let res = await Network.post(`${this.baseUrl}/tools/submit_ajax.ashx?action=user_login`, headers, body);
|
||||||
|
if (res.status !== 200) {
|
||||||
|
throw "Invalid status code: " + res.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
let setCookieHeader = res.headers['set-cookie'];
|
||||||
|
if (!setCookieHeader) {
|
||||||
|
throw "Set-Cookie header not found";
|
||||||
|
}
|
||||||
|
|
||||||
|
let cookies = Array.isArray(setCookieHeader) ? setCookieHeader : [setCookieHeader];
|
||||||
|
let myCookie = null;
|
||||||
|
|
||||||
|
for (let cookie of cookies) {
|
||||||
|
let match = cookie.match(/my=([^;]+)/);
|
||||||
|
if (match) {
|
||||||
|
myCookie = match[1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!myCookie) {
|
||||||
|
throw "my cookie not found in Set-Cookie header";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.saveData('mhg_cookie', "my="+myCookie);
|
||||||
|
return "ok";
|
||||||
|
},
|
||||||
|
|
||||||
|
logout: function() {
|
||||||
|
this.deleteData('mhg_cookie');
|
||||||
|
},
|
||||||
|
|
||||||
|
registerWebsite: "https://www.manhuagui.com/user/register"
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
isAppVersionAfter(target) {
|
isAppVersionAfter(target) {
|
||||||
if (!APP || !APP.version) return false;
|
if (!APP || !APP.version) return false;
|
||||||
let current = APP.version;
|
let current = APP.version;
|
||||||
@@ -32,9 +77,10 @@ class ManHuaGui extends ComicSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getHtml(url) {
|
async getHtml(url) {
|
||||||
|
let mhg_cookie = this.loadData("mhg_cookie");
|
||||||
let headers = {
|
let headers = {
|
||||||
accept:
|
accept:
|
||||||
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
||||||
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
||||||
"cache-control": "no-cache",
|
"cache-control": "no-cache",
|
||||||
pragma: "no-cache",
|
pragma: "no-cache",
|
||||||
@@ -48,9 +94,9 @@ class ManHuaGui extends ComicSource {
|
|||||||
"sec-fetch-site": "same-origin",
|
"sec-fetch-site": "same-origin",
|
||||||
"sec-fetch-user": "?1",
|
"sec-fetch-user": "?1",
|
||||||
"upgrade-insecure-requests": "1",
|
"upgrade-insecure-requests": "1",
|
||||||
cookie: "country=US",
|
|
||||||
Referer: "https://www.manhuagui.com/",
|
Referer: "https://www.manhuagui.com/",
|
||||||
"Referrer-Policy": "strict-origin-when-cross-origin",
|
"Referrer-Policy": "strict-origin-when-cross-origin",
|
||||||
|
cookie: mhg_cookie
|
||||||
};
|
};
|
||||||
let res = await Network.get(url, headers);
|
let res = await Network.get(url, headers);
|
||||||
if (res.status !== 200) {
|
if (res.status !== 200) {
|
||||||
@@ -60,15 +106,30 @@ class ManHuaGui extends ComicSource {
|
|||||||
return document;
|
return document;
|
||||||
}
|
}
|
||||||
parseSimpleComic(e) {
|
parseSimpleComic(e) {
|
||||||
let url = e.querySelector(".ell > a").attributes["href"];
|
let urlElement = e.querySelector(".ell > a");
|
||||||
|
if (!urlElement) {
|
||||||
|
console.warn("parseSimpleComic: Missing .ell > a element");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let url = urlElement.attributes["href"];
|
||||||
let id = url.split("/")[2];
|
let id = url.split("/")[2];
|
||||||
let title = e.querySelector(".ell > a").text.trim();
|
let title = urlElement.text.trim();
|
||||||
let cover = e.querySelector("img").attributes["src"];
|
|
||||||
|
let imgElement = e.querySelector("img");
|
||||||
|
if (!imgElement) {
|
||||||
|
console.warn("parseSimpleComic: Missing img element");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let cover = imgElement.attributes["src"] || imgElement.attributes["data-src"];
|
||||||
if (!cover) {
|
if (!cover) {
|
||||||
cover = e.querySelector("img").attributes["data-src"];
|
console.warn("parseSimpleComic: Missing cover attribute");
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
cover = `https:${cover}`;
|
cover = `https:${cover}`;
|
||||||
let description = e.querySelector(".tt").text.trim();
|
|
||||||
|
let descriptionElement = e.querySelector(".tt");
|
||||||
|
let description = descriptionElement ? descriptionElement.text.trim() : "";
|
||||||
|
|
||||||
return new Comic({
|
return new Comic({
|
||||||
id,
|
id,
|
||||||
title,
|
title,
|
||||||
@@ -81,7 +142,6 @@ class ManHuaGui extends ComicSource {
|
|||||||
let simple = this.parseSimpleComic(e);
|
let simple = this.parseSimpleComic(e);
|
||||||
let sl = e.querySelector(".sl");
|
let sl = e.querySelector(".sl");
|
||||||
let status = sl ? "连载" : "完结";
|
let status = sl ? "连载" : "完结";
|
||||||
// 如果能够找到 <span class="updateon">更新于:2020-03-31<em>3.9</em></span> 解析 更新和评分
|
|
||||||
let tmp = e.querySelector(".updateon").childNodes;
|
let tmp = e.querySelector(".updateon").childNodes;
|
||||||
let update = tmp[0].replace("更新于:", "").trim();
|
let update = tmp[0].replace("更新于:", "").trim();
|
||||||
let tags = [status, update];
|
let tags = [status, update];
|
||||||
@@ -398,47 +458,42 @@ class ManHuaGui extends ComicSource {
|
|||||||
// explore page list
|
// explore page list
|
||||||
explore = [
|
explore = [
|
||||||
{
|
{
|
||||||
// title of the page.
|
|
||||||
// title is used to identify the page, it should be unique
|
|
||||||
title: "漫画柜",
|
title: "漫画柜",
|
||||||
|
type: "multiPartPage",
|
||||||
/// multiPartPage or multiPageComicList or mixed
|
|
||||||
type: "singlePageWithMultiPart",
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* load function
|
* 参考 manhuagui_explore.html,抓取“热门漫画最新更新”与 tab 板块
|
||||||
* @param page {number | null} - page number, null for `singlePageWithMultiPart` type
|
|
||||||
* @returns {{}}
|
|
||||||
* - for `multiPartPage` type, return [{title: string, comics: Comic[], viewMore: PageJumpTarget}]
|
|
||||||
* - for `multiPageComicList` type, for each page(1-based), return {comics: Comic[], maxPage: number}
|
|
||||||
* - for `mixed` type, use param `page` as index. for each index(0-based), return {data: [], maxPage: number?}, data is an array contains Comic[] or {title: string, comics: Comic[], viewMore: string?}
|
|
||||||
*/
|
*/
|
||||||
load: async (page) => {
|
load: async (page) => {
|
||||||
let document = await this.getHtml(this.baseUrl);
|
let document = await this.getHtml(this.baseUrl);
|
||||||
// log("info", this.name, `获取主页成功`);
|
let parts = [];
|
||||||
let tabs = document.querySelectorAll("#cmt-tab li");
|
|
||||||
// log("info", this.name, tabs);
|
|
||||||
let parts = document.querySelectorAll("#cmt-cont ul");
|
|
||||||
// log("info", this.name, parts);
|
|
||||||
let result = {};
|
|
||||||
// tabs len = parts len
|
|
||||||
for (let i = 0; i < tabs.length; i++) {
|
|
||||||
let title = tabs[i].text.trim();
|
|
||||||
let comics = parts[i]
|
|
||||||
.querySelectorAll("li")
|
|
||||||
.map((e) => this.parseSimpleComic(e));
|
|
||||||
result[title] = comics;
|
|
||||||
}
|
|
||||||
// log("info", this.name, result);
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
// 1. 热门漫画最新更新
|
||||||
* Only use for `multiPageComicList` type.
|
let updateSection = document.querySelector(".update-cont");
|
||||||
* `loadNext` would be ignored if `load` function is implemented.
|
if (updateSection) {
|
||||||
* @param next {string | null} - next page token, null if first page
|
let updateComics = [];
|
||||||
* @returns {Promise<{comics: Comic[], next: string?}>} - next is null if no next page.
|
let uls = updateSection.querySelectorAll("ul");
|
||||||
*/
|
for (let ul of uls) {
|
||||||
|
let comics = ul.querySelectorAll("li").map(e => this.parseSimpleComic(e)).filter(c => c);
|
||||||
|
updateComics.push(...comics);
|
||||||
|
}
|
||||||
|
if (updateComics.length > 0) {
|
||||||
|
parts.push({ title: "热门漫画最新更新", comics: updateComics });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. tab 板块(热门连载漫画、经典完结漫画、最新上架漫画、2020新番漫画)
|
||||||
|
let tabTitles = document.querySelectorAll("#cmt-tab li");
|
||||||
|
let tabParts = document.querySelectorAll("#cmt-cont ul.cover-list");
|
||||||
|
for (let i = 0; i < tabTitles.length; i++) {
|
||||||
|
let title = tabTitles[i].text.trim();
|
||||||
|
let comics = tabParts[i].querySelectorAll("li").map(e => this.parseSimpleComic(e)).filter(c => c);
|
||||||
|
if (comics.length > 0) {
|
||||||
|
parts.push({ title, comics });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts;
|
||||||
|
},
|
||||||
loadNext(next) {},
|
loadNext(next) {},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -555,6 +610,7 @@ class ManHuaGui extends ComicSource {
|
|||||||
let genre = param;
|
let genre = param;
|
||||||
let age = options[1];
|
let age = options[1];
|
||||||
let status = options[2];
|
let status = options[2];
|
||||||
|
let sort = options[3] || "index";
|
||||||
// log(
|
// log(
|
||||||
// "info",
|
// "info",
|
||||||
// this.name,
|
// this.name,
|
||||||
@@ -563,7 +619,7 @@ class ManHuaGui extends ComicSource {
|
|||||||
// 字符串之间用“_”连接,空字符串除外
|
// 字符串之间用“_”连接,空字符串除外
|
||||||
let params = [area, genre, age, status].filter((e) => e != "").join("_");
|
let params = [area, genre, age, status].filter((e) => e != "").join("_");
|
||||||
|
|
||||||
let url = `${this.baseUrl}/list/${params}/index_p${page}.html`;
|
let url = `${this.baseUrl}/list/${params}/${sort}_p${page}.html`;
|
||||||
|
|
||||||
let document = await this.getHtml(url);
|
let document = await this.getHtml(url);
|
||||||
let maxPage = document
|
let maxPage = document
|
||||||
@@ -572,7 +628,8 @@ class ManHuaGui extends ComicSource {
|
|||||||
maxPage = parseInt(maxPage);
|
maxPage = parseInt(maxPage);
|
||||||
let comics = document
|
let comics = document
|
||||||
.querySelectorAll("#contList > li")
|
.querySelectorAll("#contList > li")
|
||||||
.map((e) => this.parseSimpleComic(e));
|
.map((e) => this.parseSimpleComic(e))
|
||||||
|
.filter((comic) => comic !== null); // 过滤掉 null 值
|
||||||
return {
|
return {
|
||||||
comics,
|
comics,
|
||||||
maxPage,
|
maxPage,
|
||||||
@@ -604,6 +661,9 @@ class ManHuaGui extends ComicSource {
|
|||||||
{
|
{
|
||||||
options: ["-全部", "lianzai-连载", "wanjie-完结"],
|
options: ["-全部", "lianzai-连载", "wanjie-完结"],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
options: ["update-最新更新", "index-最新发布", "view-人气最旺", "rate-评分最高"],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
ranking: {
|
ranking: {
|
||||||
// 对于单个选项,使用“-”分隔值和文本,左侧为值,右侧为文本
|
// 对于单个选项,使用“-”分隔值和文本,左侧为值,右侧为文本
|
||||||
@@ -848,7 +908,6 @@ class ManHuaGui extends ComicSource {
|
|||||||
let groupName = chapterGroups[i].text.trim();
|
let groupName = chapterGroups[i].text.trim();
|
||||||
let groupChapters = new Map();
|
let groupChapters = new Map();
|
||||||
|
|
||||||
// 获取对应的章节列表
|
|
||||||
let chapterList = document.querySelectorAll(".chapter-list")[i];
|
let chapterList = document.querySelectorAll(".chapter-list")[i];
|
||||||
if (chapterList) {
|
if (chapterList) {
|
||||||
let lis = chapterList.querySelectorAll("li");
|
let lis = chapterList.querySelectorAll("li");
|
||||||
@@ -859,32 +918,25 @@ class ManHuaGui extends ComicSource {
|
|||||||
groupChapters.set(id, title);
|
groupChapters.set(id, title);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 章节升序排列
|
|
||||||
groupChapters = new Map([...groupChapters].sort((a, b) => a[0] - b[0]));
|
groupChapters = new Map([...groupChapters].sort((a, b) => a[0] - b[0]));
|
||||||
|
|
||||||
// 将分组添加到总的章节映射中
|
|
||||||
chaptersMap.set(groupName, groupChapters);
|
chaptersMap.set(groupName, groupChapters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 兼容旧版本,如果app版本不支持多分组,则合并所有分组
|
|
||||||
let chapters;
|
let chapters;
|
||||||
if (this.isAppVersionAfter && this.isAppVersionAfter("1.3.0")) {
|
if (this.isAppVersionAfter && this.isAppVersionAfter("1.3.0")) {
|
||||||
// 支持多分组
|
|
||||||
chapters = chaptersMap;
|
chapters = chaptersMap;
|
||||||
} else {
|
} else {
|
||||||
// 合并所有分组
|
|
||||||
chapters = new Map();
|
chapters = new Map();
|
||||||
for (let [_, groupChapters] of chaptersMap) {
|
for (let [_, groupChapters] of chaptersMap) {
|
||||||
for (let [id, title] of groupChapters) {
|
for (let [id, title] of groupChapters) {
|
||||||
chapters.set(id, title);
|
chapters.set(id, title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 章节升序
|
|
||||||
chapters = new Map([...chapters].sort((a, b) => a[0] - b[0]));
|
chapters = new Map([...chapters].sort((a, b) => a[0] - b[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
//ANCHOR - 推荐
|
|
||||||
let recommend = [];
|
let recommend = [];
|
||||||
let similar = document.querySelector(".similar-list");
|
let similar = document.querySelector(".similar-list");
|
||||||
if (similar) {
|
if (similar) {
|
||||||
@@ -919,7 +971,6 @@ class ManHuaGui extends ComicSource {
|
|||||||
let script = document.querySelectorAll("script")[4].innerHTML;
|
let script = document.querySelectorAll("script")[4].innerHTML;
|
||||||
let infos = this.getImgInfos(script);
|
let infos = this.getImgInfos(script);
|
||||||
|
|
||||||
// https://us.hamreus.com/ps3/y/yiquanchaoren/第190话重制版/003.jpg.webp?e=1754143606&m=DPpelwkhr-pS3OXJpS6VkQ
|
|
||||||
let imgDomain = `https://us.hamreus.com`;
|
let imgDomain = `https://us.hamreus.com`;
|
||||||
let images = [];
|
let images = [];
|
||||||
for (let f of infos.files) {
|
for (let f of infos.files) {
|
||||||
@@ -927,7 +978,6 @@ class ManHuaGui extends ComicSource {
|
|||||||
imgDomain + infos.path + f + `?e=${infos.sl.e}&m=${infos.sl.m}`;
|
imgDomain + infos.path + f + `?e=${infos.sl.e}&m=${infos.sl.m}`;
|
||||||
images.push(imgUrl);
|
images.push(imgUrl);
|
||||||
}
|
}
|
||||||
// log("warning", this.name, images);
|
|
||||||
return {
|
return {
|
||||||
images,
|
images,
|
||||||
};
|
};
|
||||||
@@ -993,5 +1043,307 @@ class ManHuaGui extends ComicSource {
|
|||||||
headers,
|
headers,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [Optional] load comments
|
||||||
|
*
|
||||||
|
* Since app version 1.0.6, rich text is supported in comments.
|
||||||
|
* Following html tags are supported: ['a', 'b', 'i', 'u', 's', 'br', 'span', 'img'].
|
||||||
|
* span tag supports style attribute, but only support font-weight, font-style, text-decoration.
|
||||||
|
* All images will be placed at the end of the comment.
|
||||||
|
* Auto link detection is enabled, but only http/https links are supported.
|
||||||
|
* @param comicId {string}
|
||||||
|
* @param subId {string?} - ComicDetails.subId
|
||||||
|
* @param page {number}
|
||||||
|
* @param replyTo {string?} - commentId to reply, not null when reply to a comment
|
||||||
|
* @returns {Promise<{comments: Comment[], maxPage: number?}>}
|
||||||
|
*/
|
||||||
|
loadComments: async (comicId, subId, page, replyTo) => {
|
||||||
|
if(replyTo){
|
||||||
|
page = replyTo.split('//')[1];
|
||||||
|
replyTo = replyTo.split('//')[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
let url = `${this.baseUrl}/tools/submit_ajax.ashx?action=comment_list&book_id=${comicId}&page_index=${page}`;
|
||||||
|
|
||||||
|
let headers = {
|
||||||
|
accept: "application/json, text/javascript, */*; q=0.01",
|
||||||
|
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
||||||
|
"cache-control": "no-cache",
|
||||||
|
pragma: "no-cache",
|
||||||
|
"sec-ch-ua": '"Microsoft Edge";v="137", "Chromium";v="137", "Not/A)Brand";v="24"',
|
||||||
|
"sec-ch-ua-mobile": "?0",
|
||||||
|
"sec-ch-ua-platform": '"Windows"',
|
||||||
|
"sec-fetch-dest": "empty",
|
||||||
|
"sec-fetch-mode": "cors",
|
||||||
|
"sec-fetch-site": "same-origin",
|
||||||
|
"x-requested-with": "XMLHttpRequest",
|
||||||
|
Referer: `${this.baseUrl}/comic/${comicId}/`,
|
||||||
|
"Referrer-Policy": "strict-origin-when-cross-origin",
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = await Network.get(url, headers);
|
||||||
|
|
||||||
|
if (res.status !== 200) {
|
||||||
|
throw `获取评论失败,状态码: ${res.status}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = JSON.parse(res.body);
|
||||||
|
|
||||||
|
const replyChains = new Map();
|
||||||
|
const isSubReply = new Set();
|
||||||
|
const replyToMap = new Map();
|
||||||
|
|
||||||
|
if (data.commentIds && data.commentIds.length > 0) {
|
||||||
|
for (let commentIdString of data.commentIds) {
|
||||||
|
const commentIds = commentIdString.split(',');
|
||||||
|
if (commentIds.length > 1) {
|
||||||
|
const mainCommentId = commentIds[commentIds.length - 1];
|
||||||
|
|
||||||
|
if (!replyChains.has(mainCommentId)) {
|
||||||
|
replyChains.set(mainCommentId, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < commentIds.length - 1; i++) {
|
||||||
|
const replyId = commentIds[i];
|
||||||
|
isSubReply.add(replyId); // 标记为子回复
|
||||||
|
|
||||||
|
if (!replyChains.get(mainCommentId).includes(replyId)) {
|
||||||
|
replyChains.get(mainCommentId).push(replyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetId = (i === 0) ? mainCommentId : commentIds[i + 1];
|
||||||
|
replyToMap.set(replyId, targetId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const commentList = [];
|
||||||
|
|
||||||
|
if (data.comments) {
|
||||||
|
if (replyTo) {
|
||||||
|
const replies = [...(replyChains.get(replyTo) || [])].reverse();
|
||||||
|
|
||||||
|
for (let replyId of replies) {
|
||||||
|
const comment = data.comments[replyId];
|
||||||
|
if (comment) {
|
||||||
|
const directReplyToId = replyToMap.get(replyId);
|
||||||
|
let replyUserName = "";
|
||||||
|
if (directReplyToId && directReplyToId !== replyTo && data.comments[directReplyToId]) {
|
||||||
|
replyUserName = data.comments[directReplyToId].user_name || "匿名用户";
|
||||||
|
}
|
||||||
|
|
||||||
|
commentList.push(new Comment({
|
||||||
|
id: `${comment.id}//${page}`,
|
||||||
|
userName: replyUserName ?
|
||||||
|
`${comment.user_name || "匿名用户"} ☞ ${replyUserName}` :
|
||||||
|
comment.user_name || "匿名用户",
|
||||||
|
avatar: comment.avatar ? `https:${comment.avatar}` : "https://cf.mhgui.com/images/default.png",
|
||||||
|
content: comment.content ? comment.content : "已隐藏评论",
|
||||||
|
time: comment.add_time,
|
||||||
|
replyCount: 0, // 回复的回复暂不支持
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const mainComments = [];
|
||||||
|
for (const [id, comment] of Object.entries(data.comments)) {
|
||||||
|
if (!isSubReply.has(id)) {
|
||||||
|
const replyCount = replyChains.has(id) ? replyChains.get(id).length : (comment.reply_count || 0);
|
||||||
|
mainComments.push(new Comment({
|
||||||
|
id: `${comment.id}//${page}`,
|
||||||
|
userName: comment.user_name || "匿名用户",
|
||||||
|
avatar: comment.avatar ? `https:${comment.avatar}` : "https://cf.mhgui.com/images/default.png",
|
||||||
|
content: comment.content ? comment.content : "已隐藏评论",
|
||||||
|
time: comment.add_time,
|
||||||
|
replyCount: replyCount,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commentList.push(...mainComments.reverse());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
comments: commentList,
|
||||||
|
maxPage: replyTo ? 1 : (Math.ceil(data.total / 10) || 1)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理标签点击事件
|
||||||
|
* @param namespace {string} 标签命名空间
|
||||||
|
* @param tag {string} 标签名称
|
||||||
|
* @returns {Object} 跳转操作
|
||||||
|
*/
|
||||||
|
onClickTag: (namespace, tag) => {
|
||||||
|
// 点击类型标签时,跳转到对应的分类页面
|
||||||
|
if (namespace === "类型") {
|
||||||
|
// 根据标签查找对应的参数值
|
||||||
|
const categoryPart = this.category.parts.find(part => part.name === "类型");
|
||||||
|
if (categoryPart) {
|
||||||
|
const index = categoryPart.categories.findIndex(cat => cat === tag);
|
||||||
|
if (index !== -1) {
|
||||||
|
const param = categoryPart.categoryParams[index];
|
||||||
|
return {
|
||||||
|
action: 'category',
|
||||||
|
keyword: tag,
|
||||||
|
param: param
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (namespace === "作者") {
|
||||||
|
return {
|
||||||
|
action: 'search',
|
||||||
|
keyword: tag,
|
||||||
|
param: tag
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认返回null,表示不处理此类标签点击
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
/// favorites related
|
||||||
|
favorites = {
|
||||||
|
multiFolder: false,
|
||||||
|
/**
|
||||||
|
* load comics of the favorites
|
||||||
|
* @param page {number} - page number
|
||||||
|
* @param folder {string?} - folder name, unused for now
|
||||||
|
* @returns {Promise<{comics: Comic[], maxPage: number}>}
|
||||||
|
*/
|
||||||
|
loadComics: async (page, folder) => {
|
||||||
|
let mhg_cookie = this.loadData("mhg_cookie");
|
||||||
|
if (!mhg_cookie) {
|
||||||
|
throw "请先登录漫画柜账号";
|
||||||
|
}
|
||||||
|
let url = `${this.baseUrl}/user/book/shelf/${page}`;
|
||||||
|
let document = await this.getHtml(url);
|
||||||
|
let comicElements = document.querySelectorAll('.dy_content_li');
|
||||||
|
let comics = [];
|
||||||
|
for (let el of comicElements) {
|
||||||
|
let a = el.querySelector('.dy_img a');
|
||||||
|
if (!a) continue;
|
||||||
|
let href = a.attributes['href'];
|
||||||
|
let id = href.split('/')[2];
|
||||||
|
let img = a.querySelector('img');
|
||||||
|
let cover = img ? (img.attributes['src'] || img.attributes['data-src']) : '';
|
||||||
|
if (cover && !cover.startsWith('http')) cover = 'https:' + cover;
|
||||||
|
// dy_r 解析详细信息
|
||||||
|
let dy_r = el.querySelector('.dy_r');
|
||||||
|
let title = '';
|
||||||
|
let updateTitle = '';
|
||||||
|
let updateChapter = '';
|
||||||
|
let updateDate = '';
|
||||||
|
let lastReadChapter = '';
|
||||||
|
let lastReadDate = '';
|
||||||
|
if (dy_r) {
|
||||||
|
// 标题
|
||||||
|
let h3 = dy_r.querySelector('h3');
|
||||||
|
if (h3) {
|
||||||
|
let h3a = h3.querySelector('a');
|
||||||
|
if (h3a) title = h3a.text.trim();
|
||||||
|
}
|
||||||
|
// 更新内容
|
||||||
|
let pList = dy_r.querySelectorAll('p');
|
||||||
|
if (pList.length > 0) {
|
||||||
|
let updateP = pList[0];
|
||||||
|
let updateEm = updateP.querySelectorAll('em');
|
||||||
|
if (updateEm.length > 0) {
|
||||||
|
let chapterA = updateEm[0].querySelector('a');
|
||||||
|
if (chapterA) updateChapter = chapterA.text.trim();
|
||||||
|
updateDate = updateEm.length > 1 ? updateEm[1].text.trim() : '';
|
||||||
|
}
|
||||||
|
updateTitle = updateP.text.replace(/更新内容:/, '').trim();
|
||||||
|
}
|
||||||
|
// 最近阅读
|
||||||
|
if (pList.length > 1) {
|
||||||
|
let readP = pList[1];
|
||||||
|
let readEm = readP.querySelectorAll('em');
|
||||||
|
if (readEm.length > 0) {
|
||||||
|
let lastA = readEm[0].querySelector('a');
|
||||||
|
if (lastA) lastReadChapter = lastA.text.trim();
|
||||||
|
lastReadDate = readEm.length > 1 ? readEm[1].text.trim() : '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 兼容无dy_r时的title
|
||||||
|
if (!title) {
|
||||||
|
if (a.attributes['title']) {
|
||||||
|
title = a.attributes['title'];
|
||||||
|
} else {
|
||||||
|
title = a.text.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// tags 信息
|
||||||
|
let tags = [];
|
||||||
|
if (updateChapter) tags.push(`更新:${updateChapter}`);
|
||||||
|
if (updateDate) tags.push(`更新日期:${updateDate}`);
|
||||||
|
if (lastReadChapter) tags.push(`最近阅读:${lastReadChapter}`);
|
||||||
|
if (lastReadDate) tags.push(`最近阅读时间:${lastReadDate}`);
|
||||||
|
comics.push(new Comic({
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
subTitle: updateChapter || updateTitle || '',
|
||||||
|
cover,
|
||||||
|
description: '',
|
||||||
|
tags,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
// 页码信息
|
||||||
|
let maxPage = 1;
|
||||||
|
// 优先用“共N记录”计算
|
||||||
|
let recordInfo = document.querySelector('.flickr.right span');
|
||||||
|
if (recordInfo) {
|
||||||
|
let match = recordInfo.text.match(/共(\d+)记录/);
|
||||||
|
if (match) {
|
||||||
|
let total = parseInt(match[1], 10);
|
||||||
|
maxPage = Math.ceil(total / 20);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 兼容旧逻辑
|
||||||
|
let pageBtns = document.querySelectorAll('.page-btns a');
|
||||||
|
for (let btn of pageBtns) {
|
||||||
|
let num = parseInt(btn.text.trim(), 10);
|
||||||
|
if (!isNaN(num) && num > maxPage) maxPage = num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
comics,
|
||||||
|
maxPage
|
||||||
|
};
|
||||||
|
},
|
||||||
|
addOrDelFavorite: async (comicId, folderId, isAdding, favoriteId) => {
|
||||||
|
if (!isAdding) {
|
||||||
|
throw '暂不支持取消收藏';
|
||||||
|
}
|
||||||
|
let mhg_cookie = this.loadData("mhg_cookie");
|
||||||
|
if (!mhg_cookie) {
|
||||||
|
throw "请先登录漫画柜账号";
|
||||||
|
}
|
||||||
|
let url = `${this.baseUrl}/tools/submit_ajax.ashx?action=user_book_shelf_add`;
|
||||||
|
let headers = {
|
||||||
|
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||||
|
'x-requested-with': 'XMLHttpRequest',
|
||||||
|
'referer': `${this.baseUrl}/comic/${comicId}/`,
|
||||||
|
cookie: mhg_cookie,
|
||||||
|
};
|
||||||
|
let body = `book_id=${encodeURIComponent(comicId)}`;
|
||||||
|
let res = await Network.post(url, headers, body);
|
||||||
|
if (res.status !== 200) {
|
||||||
|
throw `添加收藏失败,状态码: ${res.status}`;
|
||||||
|
}
|
||||||
|
let data = {};
|
||||||
|
try {
|
||||||
|
data = JSON.parse(res.body);
|
||||||
|
} catch (e) {}
|
||||||
|
if (data.state !== true && data.state !== 1) {
|
||||||
|
throw data.msg || '添加收藏失败';
|
||||||
|
}
|
||||||
|
return 'ok';
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user