mirror of
https://github.com/venera-app/venera-configs.git
synced 2025-09-27 00:27:23 +00:00
包子漫画水印去除 (#114)
* refactor(baozi): 格式化代码并优化漫画章节解析逻辑 * feat: 更新包子漫画图片代理地址以优化水印问题 将图片地址从s1.baozicdn.com替换为as-rsa1-usla.baozicdn.com/w640,减少代理后的图片水印 * chore: 更新包子漫画插件版本至1.1.0 * feat(图片加载): 添加图片加载时的自定义请求头 为漫画图片加载添加自定义请求头,包括User-Agent等信息,以适配服务器要求 * refactor(图片代理): 优化移动端图片代理逻辑并移除无用代码 - 使用正则匹配简化图片URL替换逻辑 - 移除不再使用的onImageLoad方法
This commit is contained in:
836
baozi.js
836
baozi.js
@@ -1,386 +1,494 @@
|
|||||||
class Baozi extends ComicSource {
|
class Baozi extends ComicSource {
|
||||||
// 此漫画源的名称
|
// 此漫画源的名称
|
||||||
name = "包子漫画"
|
name = "包子漫画";
|
||||||
|
|
||||||
// 唯一标识符
|
// 唯一标识符
|
||||||
key = "baozi"
|
key = "baozi";
|
||||||
|
|
||||||
version = "1.0.5"
|
version = "1.1.0";
|
||||||
|
|
||||||
minAppVersion = "1.0.0"
|
minAppVersion = "1.0.0";
|
||||||
|
|
||||||
// 更新链接
|
// 更新链接
|
||||||
url = "https://git.nyne.dev/nyne/venera-configs/raw/branch/main/baozi.js"
|
url = "https://git.nyne.dev/nyne/venera-configs/raw/branch/main/baozi.js";
|
||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
language: {
|
language: {
|
||||||
title: "简繁切换",
|
title: "简繁切换",
|
||||||
type: "select",
|
type: "select",
|
||||||
options: [
|
options: [
|
||||||
{ value: "cn", text: "简体" },
|
{ value: "cn", text: "简体" },
|
||||||
{ value: "tw", text: "繁體" }
|
{ value: "tw", text: "繁體" },
|
||||||
],
|
],
|
||||||
default: "cn"
|
default: "cn",
|
||||||
|
},
|
||||||
|
domains: {
|
||||||
|
title: "主域名",
|
||||||
|
type: "select",
|
||||||
|
options: [
|
||||||
|
{ value: "baozimhcn.com" },
|
||||||
|
{ value: "webmota.com" },
|
||||||
|
{ value: "kukuc.co" },
|
||||||
|
{ value: "twmanga.com" },
|
||||||
|
{ value: "dinnerku.com" },
|
||||||
|
],
|
||||||
|
default: "baozimhcn.com",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// 动态生成完整域名
|
||||||
|
get lang() {
|
||||||
|
return this.loadSetting("language") || this.settings.language.default;
|
||||||
|
}
|
||||||
|
get baseUrl() {
|
||||||
|
let domain = this.loadSetting("domains") || this.settings.domains.default;
|
||||||
|
return `https://${this.lang}.${domain}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 账号
|
||||||
|
/// 设置为null禁用账号功能
|
||||||
|
account = {
|
||||||
|
/// 登录
|
||||||
|
/// 返回任意值表示登录成功
|
||||||
|
login: async (account, pwd) => {
|
||||||
|
let res = await Network.post(
|
||||||
|
`${this.baseUrl}/api/bui/signin`,
|
||||||
|
{
|
||||||
|
"content-type":
|
||||||
|
"multipart/form-data; boundary=----WebKitFormBoundaryFUNUxpOwyUaDop8s",
|
||||||
},
|
},
|
||||||
domains: {
|
'------WebKitFormBoundaryFUNUxpOwyUaDop8s\r\nContent-Disposition: form-data; name="username"\r\n\r\n' +
|
||||||
title: "主域名",
|
account +
|
||||||
type: "select",
|
'\r\n------WebKitFormBoundaryFUNUxpOwyUaDop8s\r\nContent-Disposition: form-data; name="password"\r\n\r\n' +
|
||||||
options: [
|
pwd +
|
||||||
{ value: "baozimhcn.com" },
|
"\r\n------WebKitFormBoundaryFUNUxpOwyUaDop8s--\r\n"
|
||||||
{ value: "webmota.com" },
|
);
|
||||||
{ value: "kukuc.co" },
|
if (res.status !== 200) {
|
||||||
{ value: "twmanga.com" },
|
throw "Invalid status code: " + res.status;
|
||||||
{ value: "dinnerku.com" }
|
}
|
||||||
],
|
let json = JSON.parse(res.body);
|
||||||
default: "baozimhcn.com"
|
let token = json.data;
|
||||||
|
Network.setCookies(this.baseUrl, [
|
||||||
|
new Cookie({
|
||||||
|
name: "TSID",
|
||||||
|
value: token,
|
||||||
|
domain: this.loadSetting("domains") || this.settings.domains.default,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
return "ok";
|
||||||
|
},
|
||||||
|
|
||||||
|
// 退出登录时将会调用此函数
|
||||||
|
logout: function () {
|
||||||
|
Network.deleteCookies(
|
||||||
|
this.loadSetting("domains") || this.settings.domains.default
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
get registerWebsite() {
|
||||||
|
return `${this.baseUrl}/user/signup`;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 解析漫画列表
|
||||||
|
parseComic(e) {
|
||||||
|
let url = e.querySelector("a").attributes["href"];
|
||||||
|
let id = url.split("/").pop();
|
||||||
|
let title = e.querySelector("h3").text.trim();
|
||||||
|
let cover = e.querySelector("a > amp-img").attributes["src"];
|
||||||
|
let tags = e.querySelectorAll("div.tabs > span").map((e) => e.text.trim());
|
||||||
|
let description = e.querySelector("small").text.trim();
|
||||||
|
return {
|
||||||
|
id: id,
|
||||||
|
title: title,
|
||||||
|
cover: cover,
|
||||||
|
tags: tags,
|
||||||
|
description: description,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
parseJsonComic(e) {
|
||||||
|
return {
|
||||||
|
id: e.comic_id,
|
||||||
|
title: e.name,
|
||||||
|
subTitle: e.author,
|
||||||
|
cover: `https://static-tw.baozimh.com/cover/${e.topic_img}?w=285&h=375&q=100`,
|
||||||
|
tags: e.type_names,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 探索页面
|
||||||
|
/// 一个漫画源可以有多个探索页面
|
||||||
|
explore = [
|
||||||
|
{
|
||||||
|
/// 标题
|
||||||
|
/// 标题同时用作标识符, 不能重复
|
||||||
|
title: "包子漫画",
|
||||||
|
|
||||||
|
/// singlePageWithMultiPart 或者 multiPageComicList
|
||||||
|
type: "singlePageWithMultiPart",
|
||||||
|
|
||||||
|
load: async () => {
|
||||||
|
var res = await Network.get(this.baseUrl);
|
||||||
|
if (res.status !== 200) {
|
||||||
|
throw "Invalid status code: " + res.status;
|
||||||
}
|
}
|
||||||
}
|
let document = new HtmlDocument(res.body);
|
||||||
|
let parts = document.querySelectorAll("div.index-recommend-items");
|
||||||
// 动态生成完整域名
|
let result = {};
|
||||||
get lang() {
|
for (let part of parts) {
|
||||||
return this.loadSetting('language') || this.settings.language.default;
|
let title = part.querySelector("div.catalog-title").text.trim();
|
||||||
}
|
let comics = part
|
||||||
get baseUrl() {
|
.querySelectorAll("div.comics-card")
|
||||||
let domain = this.loadSetting('domains') || this.settings.domains.default;
|
.map((e) => this.parseComic(e));
|
||||||
return `https://${this.lang}.${domain}`;
|
if (comics.length > 0) {
|
||||||
}
|
result[title] = comics;
|
||||||
|
}
|
||||||
/// 账号
|
|
||||||
/// 设置为null禁用账号功能
|
|
||||||
account = {
|
|
||||||
/// 登录
|
|
||||||
/// 返回任意值表示登录成功
|
|
||||||
login: async (account, pwd) => {
|
|
||||||
let res = await Network.post(`${this.baseUrl}/api/bui/signin`, {
|
|
||||||
'content-type': 'multipart/form-data; boundary=----WebKitFormBoundaryFUNUxpOwyUaDop8s'
|
|
||||||
}, "------WebKitFormBoundaryFUNUxpOwyUaDop8s\r\nContent-Disposition: form-data; name=\"username\"\r\n\r\n" + account + "\r\n------WebKitFormBoundaryFUNUxpOwyUaDop8s\r\nContent-Disposition: form-data; name=\"password\"\r\n\r\n" + pwd + "\r\n------WebKitFormBoundaryFUNUxpOwyUaDop8s--\r\n")
|
|
||||||
if (res.status !== 200) {
|
|
||||||
throw "Invalid status code: " + res.status
|
|
||||||
}
|
|
||||||
let json = JSON.parse(res.body)
|
|
||||||
let token = json.data
|
|
||||||
Network.setCookies(this.baseUrl, [
|
|
||||||
new Cookie({
|
|
||||||
name: 'TSID',
|
|
||||||
value: token,
|
|
||||||
domain: this.loadSetting('domains') || this.settings.domains.default
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
return 'ok'
|
|
||||||
},
|
|
||||||
|
|
||||||
// 退出登录时将会调用此函数
|
|
||||||
logout: function () {
|
|
||||||
Network.deleteCookies(this.loadSetting('domains') || this.settings.domains.default)
|
|
||||||
},
|
|
||||||
|
|
||||||
get registerWebsite() {
|
|
||||||
return `${this.baseUrl}/user/signup`
|
|
||||||
}
|
}
|
||||||
}
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/// 分类页面
|
||||||
|
/// 一个漫画源只能有一个分类页面, 也可以没有, 设置为null禁用分类页面
|
||||||
|
category = {
|
||||||
|
/// 标题, 同时为标识符, 不能与其他漫画源的分类页面重复
|
||||||
|
title: "包子漫画",
|
||||||
|
parts: [
|
||||||
|
{
|
||||||
|
name: "类型",
|
||||||
|
|
||||||
|
// fixed 或者 random
|
||||||
|
// random用于分类数量相当多时, 随机显示其中一部分
|
||||||
|
type: "fixed",
|
||||||
|
|
||||||
|
// 如果类型为random, 需要提供此字段, 表示同时显示的数量
|
||||||
|
// randomNumber: 5,
|
||||||
|
|
||||||
|
categories: [
|
||||||
|
"全部",
|
||||||
|
"恋爱",
|
||||||
|
"纯爱",
|
||||||
|
"古风",
|
||||||
|
"异能",
|
||||||
|
"悬疑",
|
||||||
|
"剧情",
|
||||||
|
"科幻",
|
||||||
|
"奇幻",
|
||||||
|
"玄幻",
|
||||||
|
"穿越",
|
||||||
|
"冒险",
|
||||||
|
"推理",
|
||||||
|
"武侠",
|
||||||
|
"格斗",
|
||||||
|
"战争",
|
||||||
|
"热血",
|
||||||
|
"搞笑",
|
||||||
|
"大女主",
|
||||||
|
"都市",
|
||||||
|
"总裁",
|
||||||
|
"后宫",
|
||||||
|
"日常",
|
||||||
|
"韩漫",
|
||||||
|
"少年",
|
||||||
|
"其它",
|
||||||
|
],
|
||||||
|
|
||||||
|
// category或者search
|
||||||
|
// 如果为category, 点击后将进入分类漫画页面, 使用下方的`categoryComics`加载漫画
|
||||||
|
// 如果为search, 将进入搜索页面
|
||||||
|
itemType: "category",
|
||||||
|
|
||||||
|
// 若提供, 数量需要和`categories`一致, `categoryComics.load`方法将会收到此参数
|
||||||
|
categoryParams: [
|
||||||
|
"all",
|
||||||
|
"lianai",
|
||||||
|
"chunai",
|
||||||
|
"gufeng",
|
||||||
|
"yineng",
|
||||||
|
"xuanyi",
|
||||||
|
"juqing",
|
||||||
|
"kehuan",
|
||||||
|
"qihuan",
|
||||||
|
"xuanhuan",
|
||||||
|
"chuanyue",
|
||||||
|
"mouxian",
|
||||||
|
"tuili",
|
||||||
|
"wuxia",
|
||||||
|
"gedou",
|
||||||
|
"zhanzheng",
|
||||||
|
"rexie",
|
||||||
|
"gaoxiao",
|
||||||
|
"danuzhu",
|
||||||
|
"dushi",
|
||||||
|
"zongcai",
|
||||||
|
"hougong",
|
||||||
|
"richang",
|
||||||
|
"hanman",
|
||||||
|
"shaonian",
|
||||||
|
"qita",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
enableRankingPage: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 分类漫画页面, 即点击分类标签后进入的页面
|
||||||
|
categoryComics = {
|
||||||
|
load: async (category, param, options, page) => {
|
||||||
|
let res = await Network.get(
|
||||||
|
`${this.baseUrl}/api/bzmhq/amp_comic_list?type=${param}®ion=${options[0]}&state=${options[1]}&filter=%2a&page=${page}&limit=36&language=${this.lang}&__amp_source_origin=${this.baseUrl}`
|
||||||
|
);
|
||||||
|
if (res.status !== 200) {
|
||||||
|
throw "Invalid status code: " + res.status;
|
||||||
|
}
|
||||||
|
let maxPage = null;
|
||||||
|
let json = JSON.parse(res.body);
|
||||||
|
if (!json.next) {
|
||||||
|
maxPage = page;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
comics: json.items.map((e) => this.parseJsonComic(e)),
|
||||||
|
maxPage: maxPage,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// 提供选项
|
||||||
|
optionList: [
|
||||||
|
{
|
||||||
|
options: ["all-全部", "cn-国漫", "jp-日本", "kr-韩国", "en-欧美"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
options: ["all-全部", "serial-连载中", "pub-已完结"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 搜索
|
||||||
|
search = {
|
||||||
|
load: async (keyword, options, page) => {
|
||||||
|
let res = await Network.get(`${this.baseUrl}/search?q=${keyword}`);
|
||||||
|
if (res.status !== 200) {
|
||||||
|
throw "Invalid status code: " + res.status;
|
||||||
|
}
|
||||||
|
let document = new HtmlDocument(res.body);
|
||||||
|
let comics = document
|
||||||
|
.querySelectorAll("div.comics-card")
|
||||||
|
.map((e) => this.parseComic(e));
|
||||||
|
return {
|
||||||
|
comics: comics,
|
||||||
|
maxPage: 1,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
// 提供选项
|
||||||
|
optionList: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 收藏
|
||||||
|
favorites = {
|
||||||
|
/// 是否为多收藏夹
|
||||||
|
multiFolder: false,
|
||||||
|
/// 添加或者删除收藏
|
||||||
|
addOrDelFavorite: async (comicId, folderId, isAdding) => {
|
||||||
|
if (!isAdding) {
|
||||||
|
let res = await Network.post(
|
||||||
|
`${this.baseUrl}/user/operation_v2?op=del_bookmark&comic_id=${comicId}`
|
||||||
|
);
|
||||||
|
if (!res.status || res.status >= 400) {
|
||||||
|
throw "Invalid status code: " + res.status;
|
||||||
|
}
|
||||||
|
return "ok";
|
||||||
|
} else {
|
||||||
|
let res = await Network.post(
|
||||||
|
`${this.baseUrl}/user/operation_v2?op=set_bookmark&comic_id=${comicId}&chapter_slot=0`
|
||||||
|
);
|
||||||
|
if (!res.status || res.status >= 400) {
|
||||||
|
throw "Invalid status code: " + res.status;
|
||||||
|
}
|
||||||
|
return "ok";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 加载收藏夹, 仅当multiFolder为true时有效
|
||||||
|
// 当comicId不为null时, 需要同时返回包含该漫画的收藏夹
|
||||||
|
loadFolders: null,
|
||||||
|
/// 加载漫画
|
||||||
|
loadComics: async (page, folder) => {
|
||||||
|
let res = await Network.get(`${this.baseUrl}/user/my_bookshelf`);
|
||||||
|
if (res.status !== 200) {
|
||||||
|
throw "Invalid status code: " + res.status;
|
||||||
|
}
|
||||||
|
let document = new HtmlDocument(res.body);
|
||||||
|
function parseComic(e) {
|
||||||
|
let title = e.querySelector("h4 > a").text.trim();
|
||||||
|
let url = e.querySelector("h4 > a").attributes["href"];
|
||||||
|
let id = url.split("/").pop();
|
||||||
|
let author = e
|
||||||
|
.querySelector("div.info > ul")
|
||||||
|
.children[1].text.split(":")[1]
|
||||||
|
.trim();
|
||||||
|
let description = e
|
||||||
|
.querySelector("div.info > ul")
|
||||||
|
.children[4].children[0].text.trim();
|
||||||
|
|
||||||
/// 解析漫画列表
|
|
||||||
parseComic(e) {
|
|
||||||
let url = e.querySelector("a").attributes['href']
|
|
||||||
let id = url.split("/").pop()
|
|
||||||
let title = e.querySelector("h3").text.trim()
|
|
||||||
let cover = e.querySelector("a > amp-img").attributes["src"]
|
|
||||||
let tags = e.querySelectorAll("div.tabs > span").map(e => e.text.trim())
|
|
||||||
let description = e.querySelector("small").text.trim()
|
|
||||||
return {
|
return {
|
||||||
|
id: id,
|
||||||
|
title: title,
|
||||||
|
subTitle: author,
|
||||||
|
description: description,
|
||||||
|
cover: e.querySelector("amp-img").attributes["src"],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let comics = document
|
||||||
|
.querySelectorAll("div.bookshelf-items")
|
||||||
|
.map((e) => parseComic(e));
|
||||||
|
return {
|
||||||
|
comics: comics,
|
||||||
|
maxPage: 1,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 单个漫画相关
|
||||||
|
comic = {
|
||||||
|
// 加载漫画信息
|
||||||
|
loadInfo: async (id) => {
|
||||||
|
let res = await Network.get(`${this.baseUrl}/comic/${id}`);
|
||||||
|
if (res.status !== 200) {
|
||||||
|
throw "Invalid status code: " + res.status;
|
||||||
|
}
|
||||||
|
let document = new HtmlDocument(res.body);
|
||||||
|
|
||||||
|
let title = document.querySelector("h1.comics-detail__title").text.trim();
|
||||||
|
let cover = document.querySelector("div.l-content > div > div > amp-img")
|
||||||
|
.attributes["src"];
|
||||||
|
let author = document
|
||||||
|
.querySelector("h2.comics-detail__author")
|
||||||
|
.text.trim();
|
||||||
|
let tags = document
|
||||||
|
.querySelectorAll("div.tag-list > span")
|
||||||
|
.map((e) => e.text.trim());
|
||||||
|
tags = [...tags.filter((e) => e !== "")];
|
||||||
|
let updateTime = document
|
||||||
|
.querySelector("div.supporting-text > div > span > em")
|
||||||
|
?.text.trim()
|
||||||
|
.replace("(", "")
|
||||||
|
.replace(")", "");
|
||||||
|
if (!updateTime) {
|
||||||
|
const getLastChapterText = () => {
|
||||||
|
// 合并所有章节容器(处理可能存在多个列表的情况)
|
||||||
|
const containers = [
|
||||||
|
...document.querySelectorAll(
|
||||||
|
"#chapter-items, #chapters_other_list"
|
||||||
|
),
|
||||||
|
];
|
||||||
|
let allChapters = [];
|
||||||
|
containers.forEach((container) => {
|
||||||
|
const chapters = container.querySelectorAll(".comics-chapters > a");
|
||||||
|
allChapters.push(...Array.from(chapters));
|
||||||
|
});
|
||||||
|
const lastChapter = allChapters[allChapters.length - 1];
|
||||||
|
return (
|
||||||
|
lastChapter?.querySelector("div > span")?.text.trim() ||
|
||||||
|
"暂无更新信息"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
updateTime = getLastChapterText();
|
||||||
|
}
|
||||||
|
let description = document
|
||||||
|
.querySelector("p.comics-detail__desc")
|
||||||
|
.text.trim();
|
||||||
|
let chapters = new Map();
|
||||||
|
let i = 0;
|
||||||
|
for (let c of document.querySelectorAll(
|
||||||
|
"div#chapter-items > div.comics-chapters > a > div > span"
|
||||||
|
)) {
|
||||||
|
chapters.set(i.toString(), c.text.trim());
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
for (let c of document.querySelectorAll(
|
||||||
|
"div#chapters_other_list > div.comics-chapters > a > div > span"
|
||||||
|
)) {
|
||||||
|
chapters.set(i.toString(), c.text.trim());
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i === 0) {
|
||||||
|
// 将倒序的最新章节反转
|
||||||
|
const spans = Array.from(
|
||||||
|
document.querySelectorAll("div.comics-chapters > a > div > span")
|
||||||
|
).reverse();
|
||||||
|
for (let c of spans) {
|
||||||
|
chapters.set(i.toString(), c.text.trim());
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let recommend = [];
|
||||||
|
for (let c of document.querySelectorAll("div.recommend--item")) {
|
||||||
|
if (c.querySelectorAll("div.tag-comic").length > 0) {
|
||||||
|
let title = c.querySelector("span").text.trim();
|
||||||
|
let cover = c.querySelector("amp-img").attributes["src"];
|
||||||
|
let url = c.querySelector("a").attributes["href"];
|
||||||
|
let id = url.split("/").pop();
|
||||||
|
recommend.push({
|
||||||
id: id,
|
id: id,
|
||||||
title: title,
|
title: title,
|
||||||
cover: cover,
|
cover: cover,
|
||||||
tags: tags,
|
});
|
||||||
description: description
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// updateTime 将 Y年 M月 D日 转化为 Y-M-D
|
||||||
|
let updateDate = updateTime
|
||||||
|
.replace(/年/g, "-")
|
||||||
|
.replace(/月/g, "-")
|
||||||
|
.replace(/日/g, "");
|
||||||
|
|
||||||
parseJsonComic(e) {
|
return new ComicDetails({
|
||||||
return {
|
title: title,
|
||||||
id: e.comic_id,
|
cover: cover,
|
||||||
title: e.name,
|
description: description,
|
||||||
subTitle: e.author,
|
tags: {
|
||||||
cover: `https://static-tw.baozimh.com/cover/${e.topic_img}?w=285&h=375&q=100`,
|
作者: [author],
|
||||||
tags: e.type_names,
|
标签: tags,
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 探索页面
|
|
||||||
/// 一个漫画源可以有多个探索页面
|
|
||||||
explore = [{
|
|
||||||
/// 标题
|
|
||||||
/// 标题同时用作标识符, 不能重复
|
|
||||||
title: "包子漫画",
|
|
||||||
|
|
||||||
/// singlePageWithMultiPart 或者 multiPageComicList
|
|
||||||
type: "singlePageWithMultiPart",
|
|
||||||
|
|
||||||
load: async () => {
|
|
||||||
var res = await Network.get(this.baseUrl)
|
|
||||||
if (res.status !== 200) {
|
|
||||||
throw "Invalid status code: " + res.status
|
|
||||||
}
|
|
||||||
let document = new HtmlDocument(res.body)
|
|
||||||
let parts = document.querySelectorAll("div.index-recommend-items")
|
|
||||||
let result = {}
|
|
||||||
for (let part of parts) {
|
|
||||||
let title = part.querySelector("div.catalog-title").text.trim()
|
|
||||||
let comics = part.querySelectorAll("div.comics-card").map(e => this.parseComic(e))
|
|
||||||
if (comics.length > 0) {
|
|
||||||
result[title] = comics
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
/// 分类页面
|
|
||||||
/// 一个漫画源只能有一个分类页面, 也可以没有, 设置为null禁用分类页面
|
|
||||||
category = {
|
|
||||||
/// 标题, 同时为标识符, 不能与其他漫画源的分类页面重复
|
|
||||||
title: "包子漫画",
|
|
||||||
parts: [{
|
|
||||||
name: "类型",
|
|
||||||
|
|
||||||
// fixed 或者 random
|
|
||||||
// random用于分类数量相当多时, 随机显示其中一部分
|
|
||||||
type: "fixed",
|
|
||||||
|
|
||||||
// 如果类型为random, 需要提供此字段, 表示同时显示的数量
|
|
||||||
// randomNumber: 5,
|
|
||||||
|
|
||||||
categories: ['全部', '恋爱', '纯爱', '古风', '异能', '悬疑', '剧情', '科幻', '奇幻', '玄幻', '穿越', '冒险', '推理', '武侠', '格斗', '战争', '热血', '搞笑', '大女主', '都市', '总裁', '后宫', '日常', '韩漫', '少年', '其它'],
|
|
||||||
|
|
||||||
// category或者search
|
|
||||||
// 如果为category, 点击后将进入分类漫画页面, 使用下方的`categoryComics`加载漫画
|
|
||||||
// 如果为search, 将进入搜索页面
|
|
||||||
itemType: "category",
|
|
||||||
|
|
||||||
// 若提供, 数量需要和`categories`一致, `categoryComics.load`方法将会收到此参数
|
|
||||||
categoryParams: ['all', 'lianai', 'chunai', 'gufeng', 'yineng', 'xuanyi', 'juqing', 'kehuan', 'qihuan', 'xuanhuan', 'chuanyue', 'mouxian', 'tuili', 'wuxia', 'gedou', 'zhanzheng', 'rexie', 'gaoxiao', 'danuzhu', 'dushi', 'zongcai', 'hougong', 'richang', 'hanman', 'shaonian', 'qita']
|
|
||||||
}
|
|
||||||
],
|
|
||||||
enableRankingPage: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 分类漫画页面, 即点击分类标签后进入的页面
|
|
||||||
categoryComics = {
|
|
||||||
load: async (category, param, options, page) => {
|
|
||||||
let res = await Network.get(`${this.baseUrl}/api/bzmhq/amp_comic_list?type=${param}®ion=${options[0]}&state=${options[1]}&filter=%2a&page=${page}&limit=36&language=${this.lang}&__amp_source_origin=${this.baseUrl}`)
|
|
||||||
if (res.status !== 200) {
|
|
||||||
throw "Invalid status code: " + res.status
|
|
||||||
}
|
|
||||||
let maxPage = null
|
|
||||||
let json = JSON.parse(res.body)
|
|
||||||
if (!json.next) {
|
|
||||||
maxPage = page
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
comics: json.items.map(e => this.parseJsonComic(e)),
|
|
||||||
maxPage: maxPage
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
// 提供选项
|
chapters: chapters,
|
||||||
optionList: [{
|
recommend: recommend,
|
||||||
options: [
|
updateTime: updateDate,
|
||||||
"all-全部",
|
});
|
||||||
"cn-国漫",
|
},
|
||||||
"jp-日本",
|
loadEp: async (comicId, epId) => {
|
||||||
"kr-韩国",
|
const images = [];
|
||||||
"en-欧美",
|
let currentPageUrl = `${this.baseUrl}/comic/chapter/${comicId}/0_${epId}.html`;
|
||||||
],
|
let maxAttempts = 100;
|
||||||
}, {
|
|
||||||
options: [
|
|
||||||
"all-全部",
|
|
||||||
"serial-连载中",
|
|
||||||
"pub-已完结",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 搜索
|
while (maxAttempts > 0) {
|
||||||
search = {
|
const res = await Network.get(currentPageUrl);
|
||||||
load: async (keyword, options, page) => {
|
if (res.status !== 200) break;
|
||||||
let res = await Network.get(`${this.baseUrl}/search?q=${keyword}`)
|
|
||||||
if (res.status !== 200) {
|
|
||||||
throw "Invalid status code: " + res.status
|
|
||||||
}
|
|
||||||
let document = new HtmlDocument(res.body)
|
|
||||||
let comics = document.querySelectorAll("div.comics-card").map(e => this.parseComic(e))
|
|
||||||
return {
|
|
||||||
comics: comics,
|
|
||||||
maxPage: 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 提供选项
|
// 解析当前页图片
|
||||||
optionList: []
|
const doc = new HtmlDocument(res.body);
|
||||||
}
|
doc
|
||||||
|
.querySelectorAll("ul.comic-contain > div > amp-img")
|
||||||
|
.forEach((img) => {
|
||||||
|
const src = img?.attributes?.["src"];
|
||||||
|
if (typeof src === "string") images.push(src);
|
||||||
|
});
|
||||||
|
|
||||||
/// 收藏
|
// 查找下一页链接
|
||||||
favorites = {
|
const nextLink = doc.querySelector("a#next-chapter");
|
||||||
/// 是否为多收藏夹
|
if (nextLink?.text?.match(/下一页|下一頁/)) {
|
||||||
multiFolder: false,
|
currentPageUrl = nextLink.attributes["href"];
|
||||||
/// 添加或者删除收藏
|
} else {
|
||||||
addOrDelFavorite: async (comicId, folderId, isAdding) => {
|
break;
|
||||||
if (!isAdding) {
|
|
||||||
let res = await Network.post(`${this.baseUrl}/user/operation_v2?op=del_bookmark&comic_id=${comicId}`)
|
|
||||||
if (!res.status || res.status >= 400) {
|
|
||||||
throw "Invalid status code: " + res.status
|
|
||||||
}
|
|
||||||
return 'ok'
|
|
||||||
} else {
|
|
||||||
let res = await Network.post(`${this.baseUrl}/user/operation_v2?op=set_bookmark&comic_id=${comicId}&chapter_slot=0`)
|
|
||||||
if (!res.status || res.status >= 400) {
|
|
||||||
throw "Invalid status code: " + res.status
|
|
||||||
}
|
|
||||||
return 'ok'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 加载收藏夹, 仅当multiFolder为true时有效
|
|
||||||
// 当comicId不为null时, 需要同时返回包含该漫画的收藏夹
|
|
||||||
loadFolders: null,
|
|
||||||
/// 加载漫画
|
|
||||||
loadComics: async (page, folder) => {
|
|
||||||
let res = await Network.get(`${this.baseUrl}/user/my_bookshelf`)
|
|
||||||
if (res.status !== 200) {
|
|
||||||
throw "Invalid status code: " + res.status
|
|
||||||
}
|
|
||||||
let document = new HtmlDocument(res.body)
|
|
||||||
function parseComic(e) {
|
|
||||||
let title = e.querySelector("h4 > a").text.trim()
|
|
||||||
let url = e.querySelector("h4 > a").attributes['href']
|
|
||||||
let id = url.split("/").pop()
|
|
||||||
let author = e.querySelector("div.info > ul").children[1].text.split(":")[1].trim()
|
|
||||||
let description = e.querySelector("div.info > ul").children[4].children[0].text.trim()
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: id,
|
|
||||||
title: title,
|
|
||||||
subTitle: author,
|
|
||||||
description: description,
|
|
||||||
cover: e.querySelector("amp-img").attributes['src']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let comics = document.querySelectorAll("div.bookshelf-items").map(e => parseComic(e))
|
|
||||||
return {
|
|
||||||
comics: comics,
|
|
||||||
maxPage: 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
maxAttempts--;
|
||||||
|
}
|
||||||
/// 单个漫画相关
|
// 代理后图片水印更少
|
||||||
comic = {
|
let mobileImages = images.map((e) => {
|
||||||
// 加载漫画信息
|
const regex = /scomic\/.*/;
|
||||||
loadInfo: async (id) => {
|
const match = e.match(regex);
|
||||||
let res = await Network.get(`${this.baseUrl}/comic/${id}`)
|
return `https://as-rsa1-usla.baozicdn.com/w640/${match[0]}`;
|
||||||
if (res.status !== 200) {
|
});
|
||||||
throw "Invalid status code: " + res.status
|
return { images: mobileImages };
|
||||||
}
|
},
|
||||||
let document = new HtmlDocument(res.body)
|
};
|
||||||
|
|
||||||
let title = document.querySelector("h1.comics-detail__title").text.trim()
|
|
||||||
let cover = document.querySelector("div.l-content > div > div > amp-img").attributes['src']
|
|
||||||
let author = document.querySelector("h2.comics-detail__author").text.trim()
|
|
||||||
let tags = document.querySelectorAll("div.tag-list > span").map(e => e.text.trim())
|
|
||||||
tags = [...tags.filter(e => e !== "")]
|
|
||||||
let updateTime = document.querySelector("div.supporting-text > div > span > em")?.text.trim().replace('(', '').replace(')', '')
|
|
||||||
if (!updateTime) {
|
|
||||||
const getLastChapterText = () => {
|
|
||||||
// 合并所有章节容器(处理可能存在多个列表的情况)
|
|
||||||
const containers = [
|
|
||||||
...document.querySelectorAll("#chapter-items, #chapters_other_list")
|
|
||||||
];
|
|
||||||
let allChapters = [];
|
|
||||||
containers.forEach(container => {
|
|
||||||
const chapters = container.querySelectorAll(".comics-chapters > a");
|
|
||||||
allChapters.push(...Array.from(chapters));
|
|
||||||
});
|
|
||||||
const lastChapter = allChapters[allChapters.length - 1];
|
|
||||||
return lastChapter?.querySelector("div > span")?.text.trim() || "暂无更新信息";
|
|
||||||
};
|
|
||||||
updateTime = getLastChapterText();
|
|
||||||
}
|
|
||||||
let description = document.querySelector("p.comics-detail__desc").text.trim()
|
|
||||||
let chapters = new Map()
|
|
||||||
let i = 0
|
|
||||||
for (let c of document.querySelectorAll("div#chapter-items > div.comics-chapters > a > div > span")) {
|
|
||||||
chapters.set(i.toString(), c.text.trim())
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
for (let c of document.querySelectorAll("div#chapters_other_list > div.comics-chapters > a > div > span")) {
|
|
||||||
chapters.set(i.toString(), c.text.trim())
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if (i === 0) {
|
|
||||||
// 将倒序的最新章节反转
|
|
||||||
const spans = Array.from(document.querySelectorAll("div.comics-chapters > a > div > span")).reverse();
|
|
||||||
for (let c of spans) {
|
|
||||||
chapters.set(i.toString(), c.text.trim());
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let recommend = []
|
|
||||||
for (let c of document.querySelectorAll("div.recommend--item")) {
|
|
||||||
if (c.querySelectorAll("div.tag-comic").length > 0) {
|
|
||||||
let title = c.querySelector("span").text.trim()
|
|
||||||
let cover = c.querySelector("amp-img").attributes['src']
|
|
||||||
let url = c.querySelector("a").attributes['href']
|
|
||||||
let id = url.split("/").pop()
|
|
||||||
recommend.push({
|
|
||||||
id: id,
|
|
||||||
title: title,
|
|
||||||
cover: cover
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// updateTime 将 Y年 M月 D日 转化为 Y-M-D
|
|
||||||
let updateDate = updateTime.replace(/年/g, '-').replace(/月/g, '-').replace(/日/g, '');
|
|
||||||
|
|
||||||
return new ComicDetails({
|
|
||||||
title: title,
|
|
||||||
cover: cover,
|
|
||||||
description: description,
|
|
||||||
tags: {
|
|
||||||
"作者": [author],
|
|
||||||
"标签": tags
|
|
||||||
},
|
|
||||||
chapters: chapters,
|
|
||||||
recommend: recommend,
|
|
||||||
updateTime: updateDate,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
loadEp: async (comicId, epId) => {
|
|
||||||
const images = [];
|
|
||||||
let currentPageUrl = `${this.baseUrl}/comic/chapter/${comicId}/0_${epId}.html`;
|
|
||||||
let maxAttempts = 100;
|
|
||||||
|
|
||||||
while (maxAttempts > 0) {
|
|
||||||
const res = await Network.get(currentPageUrl);
|
|
||||||
if (res.status !== 200) break;
|
|
||||||
|
|
||||||
// 解析当前页图片
|
|
||||||
const doc = new HtmlDocument(res.body);
|
|
||||||
doc.querySelectorAll("ul.comic-contain > div > amp-img").forEach(img => {
|
|
||||||
const src = img?.attributes?.['src'];
|
|
||||||
if (typeof src === 'string') images.push(src);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 查找下一页链接
|
|
||||||
const nextLink = doc.querySelector("a#next-chapter");
|
|
||||||
if (nextLink?.text?.match(/下一页|下一頁/)) {
|
|
||||||
currentPageUrl = nextLink.attributes['href'];
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
maxAttempts--;
|
|
||||||
}
|
|
||||||
// 代理后图片水印更少
|
|
||||||
return { images };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
"name": "包子漫画",
|
"name": "包子漫画",
|
||||||
"fileName": "baozi.js",
|
"fileName": "baozi.js",
|
||||||
"key": "baozi",
|
"key": "baozi",
|
||||||
"version": "1.0.5"
|
"version": "1.1.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Picacg",
|
"name": "Picacg",
|
||||||
|
Reference in New Issue
Block a user