Files
venera-configs/manwaba.js
Brooklyn Bartly 6e52854782 Manwaba (#118)
* feat: 添加漫蛙漫画源实现基础功能

* refactor(api): 重构网络请求和数据处理逻辑

- 将 `fetchJson` 拆分为 `getJson` 和 `postJson` 方法,增强类型安全
- 更新基础 URL 移除尾部斜杠
- 实现分类、搜索和详情页的实际 API 调用
- 完善漫画状态显示和分类选项
- 移除冗余的 `initFunc` 方法

* refactor: 移除未实现的收藏相关功能代码

清理未实现的收藏功能相关代码,包括添加/删除收藏、加载收藏夹等功能,以保持代码库整洁

* refactor(manwaba): 重构API响应处理逻辑并实现loadInfo方法

- 修改getJson方法直接返回完整JSON响应,不再处理特定code和data字段
- 重构分类列表和搜索结果的data字段处理逻辑
- 实现loadInfo方法获取漫画详情信息

* refactor(api): 重构API请求方法并更新基础URL

- 将多个独立的请求方法合并为统一的fetchJson方法
- 更新基础URL为新的API端点
- 简化参数处理和请求逻辑
- 移除不再使用的工具方法

* fix: 将漫画ID转换为字符串类型以避免潜在的类型错误

* refactor: 将baseUrl重命名为api以提升代码可读性

统一将baseUrl变量名改为api,使其更符合实际用途,提高代码可读性和一致性

* refactor(ManWaBa): 优化fetchJson默认参数并添加日志功能

- 为fetchJson方法的payload参数添加默认值undefined
- 新增logger对象提供error/info/warn日志方法
- 在loadInfo方法中添加日志记录
- 移除未使用的可选方法以简化代码结构

* fix: 修复fetchJson调用时payload参数未定义的问题

确保在调用fetchJson时明确传递payload为undefined,避免潜在的类型错误

* refactor(ManWaBa): 优化漫画信息加载和章节图片获取逻辑

重构漫画信息加载和章节图片获取的代码,提取重复参数为变量,简化请求逻辑
移除未使用的onImageLoad和onThumbnailLoad方法,集中处理图片获取功能

* feat: 添加漫蛙吧源到index.json
2025-07-28 17:54:39 +08:00

388 lines
10 KiB
JavaScript

/** @type {import('./_venera_.js')} */
class ManWaBa 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 = "manwaba";
version = "1.0.0";
minAppVersion = "1.4.0";
// update url
url = "https://cdn.jsdelivr.net/gh/venera-app/venera-configs@main/manwaba.js";
api = "https://www.manwaba.com/api/v1";
init() {
/**
* Sends an HTTP request.
* @param {string} url - The URL to send the request to.
* @param {string} method - The HTTP method (e.g., GET, POST, PUT, PATCH, DELETE).
* @param {Object} params - The query parameters to include in the request.
* @param {Object} headers - The headers to include in the request.
* @param {string} payload - The payload to include in the request.
* @returns {Promise<Object>} The response from the request.
*/
this.fetchJson = async (
url,
{ method = "GET", params, headers, payload }
) => {
if (params) {
let params_str = Object.keys(params)
.map((key) => `${key}=${params[key]}`)
.join("&");
url += `?${params_str}`;
}
let res = await Network.sendRequest(method, url, headers, payload);
if (res.status !== 200) {
throw `Invalid status code: ${res.status}, body: ${res.body}`;
}
let json = JSON.parse(res.body);
return json;
};
this.logger = {
error: (msg) => {
log("error", this.name, msg);
},
info: (msg) => {
log("info", this.name, msg);
},
warn: (msg) => {
log("warning", this.name, msg);
},
};
}
// explore page list
explore = [
{
// title of the page.
// title is used to identify the page, it should be unique
title: this.name,
/// multiPartPage or multiPageComicList or mixed
type: "singlePageWithMultiPart",
/**
* load function
* @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) => {
let params = {
page: 1,
pageSize: 6,
type: "",
flag: false,
};
const url = `${this.api}/json/home`;
const data = await this.fetchJson(url, { params }).then(
(res) => res.data
);
let magnaList = {
热门: data.comicList,
古风: data.gufengList,
玄幻: data.xuanhuanList,
校园: data.xiaoyuanList,
};
function parseComic(comic) {
return new Comic({
id: comic.id.toString(),
title: comic.title,
subTitle: comic.author,
cover: comic.pic,
tags: comic.tags.split(","),
});
}
let result = {};
for (let key in magnaList) {
result[key] = magnaList[key].map(parseComic);
}
return result;
},
},
];
// categories
category = {
/// title of the category page, used to identify the page, it should be unique
title: this.name,
parts: [
{
// title of the part
name: "类型",
// fixed or random or dynamic
// if random, need to provide `randomNumber` field, which indicates the number of comics to display at the same time
// if dynamic, need to provide `loader` field, which indicates the function to load comics
type: "fixed",
// Remove this if type is dynamic
categories: [
"全部",
"热血",
"玄幻",
"恋爱",
"冒险",
"古风",
"都市",
"穿越",
"奇幻",
"其他",
"搞笑",
"少男",
"战斗",
"重生",
"逆袭",
"爆笑",
"少年",
"后宫",
"系统",
"BL",
"韩漫",
"完整版",
"19r",
"台版",
],
itemType: "category",
categoryParams: [
"",
"热血",
"玄幻",
"恋爱",
"冒险",
"古风",
"都市",
"穿越",
"奇幻",
"其他",
"搞笑",
"少男",
"战斗",
"重生",
"逆袭",
"爆笑",
"少年",
"后宫",
"系统",
"BL",
"韩漫",
"完整版",
"19r",
"台版",
],
},
],
// enable ranking page
enableRankingPage: false,
};
/// category comic loading related
categoryComics = {
/**
* load comics of a category
* @param category {string} - category name
* @param param {string?} - category param
* @param options {string[]} - options from optionList
* @param page {number} - page number
* @returns {Promise<{comics: Comic[], maxPage: number}>}
*/
load: async (category, param, options, page) => {
let url = `${this.api}/json/cate`;
let payload = JSON.stringify({
page: {
page: page,
pageSize: 10,
},
category: "comic",
sort: parseInt(options[2]),
comic: {
status: parseInt(options[0] == "2" ? -1 : options[0]),
day: parseInt(options[1]),
tag: param,
},
video: {
year: 0,
typeId: 0,
typeId1: 0,
area: "",
lang: "",
status: -1,
day: 0,
},
novel: {
status: -1,
day: 0,
sortId: 0,
},
});
let data = await this.fetchJson(url, {
method: "POST",
payload,
}).then((res) => res.data);
function parseComic(comic) {
return new Comic({
id: comic.url.split("/").pop(),
title: comic.title,
subTitle: comic.author,
cover: comic.pic,
tags: comic.tags.split(","),
description: comic.intro,
status: comic.status == 0 ? "连载中" : "已完结",
});
}
return {
comics: data.map(parseComic),
maxPage: 100,
};
},
// provide options for category comic loading
optionList: [
{
options: ["2-全部", "0-连载中", "1-已完结"],
},
{
options: [
"0-全部",
"1-周一",
"2-周二",
"3-周三",
"4-周四",
"5-周五",
"6-周六",
"7-周日",
],
},
{
options: ["0-更新", "1-新作", "2-畅销", "3-热门", "4-收藏"],
},
],
};
/// search related
search = {
/**
* load search result
* @param keyword {string}
* @param options {string[]} - options from optionList
* @param page {number}
* @returns {Promise<{comics: Comic[], maxPage: number}>}
*/
load: async (keyword, options, page) => {
const pageSize = 20;
let url = `${this.api}/json/search`;
let params = {
keyword,
type: "mh",
page,
pageSize,
};
let data = await this.fetchJson(url, { params }).then((res) => res.data);
let total = data.total;
let comics = data.list.map((item) => {
return new Comic({
id: item.id.toString(),
title: item.title,
subTitle: item.author,
cover: item.cover,
tags: item.tags.split(","),
description: item.description,
status: item.status == 0 ? "连载中" : "已完结",
});
});
let maxPage = Math.ceil(total / pageSize);
return {
comics,
maxPage,
};
},
};
/// single comic related
comic = {
/**
* load comic info
* @param id {string}
* @returns {Promise<ComicDetails>}s
*/
loadInfo: async (id) => {
let url = `${this.api}/json/comic/${id}`;
let data = await this.fetchJson(url, { payload: undefined }).then(
(res) => res.data
);
this.logger.warn(`loadInfo: ${data}`);
let chapterId = data.id;
let chapterApi = `${this.api}/json/comic/chapter`;
let params = {
comicId: chapterId,
page: 1,
pageSize: 1,
};
let pageRes = await this.fetchJson(chapterApi, { params });
let total = pageRes.pagination.total;
let chapterRes = await this.fetchJson(chapterApi, {
params: {
...params,
pageSize: total,
},
});
let chapterList = chapterRes.data;
let chapters = new Map();
chapterList.forEach((item) => {
chapters.set(item.id.toString(), item.title.toString());
});
return new ComicDetails({
title: data.title.toString(),
subTitle: data.author.toString(),
cover: data.cover,
tags: {
类型: data.tags.split(","),
状态: data.status == 0 ? "连载中" : "已完结",
},
chapters,
description: data.intro,
updateTime: new Date(data.editTime * 1000).toLocaleDateString(),
});
},
/**
* load images of a chapter
* @param comicId {string}
* @param epId {string?}
* @returns {Promise<{images: string[]}>}
*/
loadEp: async (comicId, epId) => {
let imgApi = `${this.api}/comic/image/${epId}`;
let params = {
page: 1,
pageSize: 1,
imageSource: "https://tu.mhttu.cc",
};
let pageNum = await this.fetchJson(imgApi, {
params,
}).then((res) => res.data.pagination.total);
let imageRes = await this.fetchJson(imgApi, {
params: {
...params,
pageSize: pageNum,
},
}).then((res) => res.data.images);
let images = imageRes.map((item) => item.url);
return {
images,
};
},
};
}