Files
venera-configs/ikmmh.js
Brooklyn Bartly e7e8324e94 feat(漫画详情): 添加更新时间字段并重构返回对象 (#104)
从meta标签获取漫画更新时间并格式化为YYYY-MM-DD
将返回对象改为ComicDetails实例,保持数据结构一致性
2025-07-22 19:07:07 +08:00

453 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

class Ikm extends ComicSource {
// 基础配置
name = "爱看漫";
key = "ikmmh";
version = "1.0.2";
minAppVersion = "1.0.0";
url = "https://git.nyne.dev/nyne/venera-configs/raw/branch/main/ikmmh.js";
// 常量定义
static baseUrl = "https://ymcdnyfqdapp.ikmmh.com";
static Mobile_UA = "Mozilla/5.0 (Linux; Android) Mobile";
static webHeaders = {
"User-Agent": Ikm.Mobile_UA,
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",
};
static jsonHead = {
"User-Agent": Ikm.Mobile_UA,
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
Accept: "application/json, text/javascript, */*; q=0.01",
"Accept-Encoding": "gzip",
"X-Requested-With": "XMLHttpRequest",
};
// 统一缩略图加载配置
static thumbConfig = (url) => ({
headers: {
...Ikm.webHeaders,
Referer: Ikm.baseUrl,
},
});
// 账号系统
account = {
login: async (account, pwd) => {
try {
let res = await Network.post(
`${Ikm.baseUrl}/api/user/userarr/login`,
Ikm.jsonHead,
`user=${account}&pass=${pwd}`
);
if (res.status !== 200)
throw new Error(`登录失败,状态码:${res.status}`);
let data = JSON.parse(res.body);
if (data.code !== 0) throw new Error(data.msg || "登录异常");
return "ok";
} catch (err) {
throw new Error(`登录失败:${err.message}`);
}
},
logout: () => Network.deleteCookies("ymcdnyfqdapp.ikmmh.com"),
registerWebsite: `${Ikm.baseUrl}/user/register/`,
};
// 探索页面
explore = [
{
title: this.name,
type: "singlePageWithMultiPart",
load: async () => {
try {
let res = await Network.get(`${Ikm.baseUrl}/`, Ikm.webHeaders);
if (res.status !== 200)
throw new Error(`加载探索页面失败,状态码:${res.status}`);
let document = new HtmlDocument(res.body);
let parseComic = (e) => {
let title = e.querySelector("div.title").text.split("~")[0];
let cover = e.querySelector("div.thumb_img").attributes["data-src"];
let link = `${Ikm.baseUrl}${
e.querySelector("a").attributes["href"]
}`;
return {
title,
cover,
id: link,
};
};
return {
本周推荐: document
.querySelectorAll("div.module-good-fir > div.item")
.map(parseComic),
今日更新: document
.querySelectorAll("div.module-day-fir > div.item")
.map(parseComic),
};
} catch (err) {
throw new Error(`探索页面加载失败:${err.message}`);
}
},
onThumbnailLoad: Ikm.thumbConfig,
},
];
// 分类页面
category = {
title: "爱看漫",
parts: [
{
name: "分类",
// fixed 或者 random
// random用于分类数量相当多时, 随机显示其中一部分
type: "fixed",
// 如果类型为random, 需要提供此字段, 表示同时显示的数量
// randomNumber: 5,
categories: [
"全部",
"长条",
"大女主",
"百合",
"耽美",
"纯爱",
"後宫",
"韩漫",
"奇幻",
"轻小说",
"生活",
"悬疑",
"格斗",
"搞笑",
"伪娘",
"竞技",
"职场",
"萌系",
"冒险",
"治愈",
"都市",
"霸总",
"神鬼",
"侦探",
"爱情",
"古风",
"欢乐向",
"科幻",
"穿越",
"性转换",
"校园",
"美食",
"悬疑",
"剧情",
"热血",
"节操",
"励志",
"异世界",
"历史",
"战争",
"恐怖",
"霸总",
"全部",
"连载中",
"已完结",
"全部",
"日漫",
"港台",
"美漫",
"国漫",
"韩漫",
"未分类",
],
// category或者search
// 如果为category, 点击后将进入分类漫画页面, 使用下方的`categoryComics`加载漫画
// 如果为search, 将进入搜索页面
itemType: "category",
},
{
name: "更新",
type: "fixed",
categories: [
"星期一",
"星期二",
"星期三",
"星期四",
"星期五",
"星期六",
"星期日",
],
itemType: "category",
categoryParams: ["1", "2", "3", "4", "5", "6", "7"],
},
],
enableRankingPage: false,
};
// 分类漫画加载
categoryComics = {
load: async (category, param, options, page) => {
try {
let res;
if (param) {
res = await Network.get(
`${Ikm.baseUrl}/update/${param}.html`,
Ikm.webHeaders
);
if (res.status !== 200)
throw new Error(`分类请求失败,状态码:${res.status}`);
let document = new HtmlDocument(res.body);
let comics = document.querySelectorAll("li.comic-item").map((e) => ({
title: e.querySelector("p.title").text.split("~")[0],
cover: e.querySelector("img").attributes["src"],
id: `${Ikm.baseUrl}${e.querySelector("a").attributes["href"]}`,
subTitle: e.querySelector("span.chapter").text,
}));
return {
comics,
maxPage: 1,
};
} else {
res = await Network.post(
`${Ikm.baseUrl}/api/comic/index/lists`,
Ikm.jsonHead,
`area=${options[1]}&tags=${encodeURIComponent(category)}&full=${
options[0]
}&page=${page}`
);
let resData = JSON.parse(res.body);
return {
comics: resData.data.map((e) => ({
id: `${Ikm.baseUrl}${e.info_url}`,
title: e.name.split("~")[0],
subTitle: e.author,
cover: e.cover,
tags: e.tags,
description: e.lastchapter,
})),
maxPage: resData.end || 1,
};
}
} catch (err) {
throw new Error(`分类加载失败:${err.message}`);
}
},
onThumbnailLoad: Ikm.thumbConfig,
optionList: [
{
// 对于单个选项, 使用-分割, 左侧为用于数据加载的值, 即传给load函数的options参数; 右侧为显示给用户的文本
options: ["3-全部", "4-连载中", "1-已完结"],
notShowWhen: [
"星期一",
"星期二",
"星期三",
"星期四",
"星期五",
"星期六",
"星期日",
],
showWhen: null,
},
{
options: [
"9-全部",
"1-日漫",
"2-港台",
"3-美漫",
"4-国漫",
"5-韩漫",
"6-未分类",
],
notShowWhen: [
"星期一",
"星期二",
"星期三",
"星期四",
"星期五",
"星期六",
"星期日",
],
showWhen: null,
},
],
};
// 搜索功能
search = {
load: async (keyword, options, page) => {
try {
let res = await Network.get(
`${Ikm.baseUrl}/search?searchkey=${encodeURIComponent(keyword)}`,
Ikm.webHeaders
);
let document = new HtmlDocument(res.body);
return {
comics: document.querySelectorAll("li.comic-item").map((e) => ({
title: e.querySelector("p.title").text.split("~")[0],
cover: e.querySelector("img").attributes["src"],
id: `${Ikm.baseUrl}${e.querySelector("a").attributes["href"]}`,
subTitle: e.querySelector("span.chapter").text,
})),
maxPage: 1,
};
} catch (err) {
throw new Error(`搜索失败:${err.message}`);
}
},
onThumbnailLoad: Ikm.thumbConfig,
optionList: [],
};
// 收藏功能
favorites = {
multiFolder: false,
addOrDelFavorite: async (comicId, folderId, isAdding) => {
try {
let id = comicId.match(/\d+/)[0];
if (isAdding) {
// 获取漫画信息
let infoRes = await Network.get(comicId, Ikm.webHeaders);
let name = new HtmlDocument(infoRes.body).querySelector(
"meta[property='og:title']"
).attributes["content"];
// 添加收藏
let res = await Network.post(
`${Ikm.baseUrl}/api/user/bookcase/add`,
Ikm.jsonHead,
`articleid=${id}&articlename=${encodeURIComponent(name)}`
);
let data = JSON.parse(res.body);
if (data.code !== "0") throw new Error(data.msg || "收藏失败");
return "ok";
} else {
// 删除收藏
let res = await Network.post(
`${Ikm.baseUrl}/api/user/bookcase/del`,
Ikm.jsonHead,
`articleid=${id}`
);
let data = JSON.parse(res.body);
if (data.code !== "0") throw new Error(data.msg || "取消收藏失败");
return "ok";
}
} catch (err) {
throw new Error(`收藏操作失败:${err.message}`);
}
},
//加载收藏
loadComics: async (page, folder) => {
let res = await Network.get(
`${Ikm.baseUrl}/user/bookcase`,
Ikm.webHeaders
);
if (res.status !== 200) {
throw "加载收藏失败:" + res.status;
}
let document = new HtmlDocument(res.body);
return {
comics: document.querySelectorAll("div.bookrack-item").map((e) => ({
title: e.querySelector("h3").text.split("~")[0],
subTitle: e.querySelector("p.desc").text,
cover: e.querySelector("img").attributes["src"],
id: `${Ikm.baseUrl}/book/${e.attributes["data-id"]}/`,
})),
maxPage: 1,
};
},
onThumbnailLoad: Ikm.thumbConfig,
};
// 漫画详情
comic = {
loadInfo: async (id) => {
let res = await Network.get(id, Ikm.webHeaders);
let document = new HtmlDocument(res.body);
let comicId = id.match(/\d+/)[0];
// 获取章节数据
let epRes = await Network.get(
`${Ikm.baseUrl}/api/comic/zyz/chapterlink?id=${comicId}`,
{
...Ikm.jsonHead,
Referer: id,
}
);
let epData = JSON.parse(epRes.body);
let eps = new Map();
if (epData.data && epData.data.length > 0 && epData.data[0].list) {
epData.data[0].list.forEach((e) => {
let title = e.name;
let id = `${Ikm.baseUrl}${e.url}`;
eps.set(id, title);
});
} else {
throw new Error(`章节数据格式异常`);
}
let title = document.querySelector(
"div.book-hero__detail > div.title"
).text;
let escapedTitle = title.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
let thumb =
document
.querySelector("div.coverimg")
.attributes["style"].match(/\((.*?)\)/)?.[1] || "";
let desc = document
.querySelector("article.book-container__detail")
.text.match(
new RegExp(
`漫画名:${escapedTitle}(?:(?:[^。]*?(?:简介|漫画简介)\\s*[:]?\\s*)|(?:[^。]*?))([\\s\\S]+?)\\.\\.\\.。`
)
);
let intro = desc?.[1]?.trim().replace(/\s+/g, " ") || "";
// 获取更新日期
let fullDateStr = document
.querySelector('meta[property="og:cartoon:update_time"]')
.attributes["content"]; // "2025-07-18 08:37:02"
let date = new Date(fullDateStr);
let year = date.getFullYear();
let month = String(date.getMonth() + 1).padStart(2, "0"); // 月份从0开始要加1
let day = String(date.getDate()).padStart(2, "0");
let updateTime = `${year}-${month}-${day}`;
return new ComicDetails({
title: title.split("~")[0],
cover: thumb,
description: intro,
updateTime: updateTime,
tags: {
作者: [
document
.querySelector("div.book-container__author")
.text.split("作者:")[1],
],
最新章节: [document.querySelector("div.update > a > em").text],
标签: document
.querySelectorAll("div.book-hero__detail > div.tags > a")
.map((e) => e.text.trim())
.filter((text) => text),
},
chapters: eps,
recommend: document
.querySelectorAll("div.module-guessu > div.item")
.map((e) => ({
title: e.querySelector("div.title").text.split("~")[0],
cover: e.querySelector("div.thumb_img").attributes["data-src"],
id: `${Ikm.baseUrl}${e.querySelector("a").attributes["href"]}`,
})),
});
},
onThumbnailLoad: Ikm.thumbConfig,
loadEp: async (comicId, epId) => {
try {
let res = await Network.get(epId, Ikm.webHeaders);
let document = new HtmlDocument(res.body);
return {
images: document
.querySelectorAll("img.lazy")
.map((e) => e.attributes["data-src"]),
};
} catch (err) {
throw new Error(`加载章节失败:${err.message}`);
}
},
onImageLoad: (url, comicId, epId) => {
return {
url,
headers: {
...Ikm.webHeaders,
Referer: epId,
},
};
},
};
}