新增GoDa漫画和18漫画 (#193)

This commit is contained in:
Yursd
2025-11-12 21:58:14 +08:00
committed by GitHub
parent 99f36ab6f2
commit a14e7552a5
3 changed files with 660 additions and 0 deletions

332
goda.js Normal file
View File

@@ -0,0 +1,332 @@
/** @type {import('./_venera_.js')} */
class Goda extends ComicSource {
// Note: The fields which are marked as [Optional] should be removed if not used
// name of the source
name = "GoDa漫画"
// unique id of the source
key = "goda"
version = "1.0.0"
minAppVersion = "1.4.0"
// update url
url = "https://git.nyne.dev/nyne/venera-configs/raw/branch/main/goda.js"
settings = {
domains: {
title: "域名",
type: "input",
default: "godamh.com"
},
api: {
title: "API域名",
type: "input",
default: "api-get-v3.mgsearcher.com"
},
image: {
title: "图片域名",
type: "input",
default: "t40-1-4.g-mh.online"
}
}
get baseUrl() {
return `https://${this.loadSetting("domains")}`;
}
get apiUrl() {
return `https://${this.loadSetting("api")}/api`;
}
get imageUrl() {
return `https://${this.loadSetting("image")}`;
}
get headers() {
return {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:144.0) Gecko/20100101 Firefox/144.0",
"Referer": this.baseUrl
};
}
parseComics(doc) {
console.warn(doc)
const result = [];
for (let item of doc.querySelectorAll(".pb-2")) {
result.push(new Comic({
id: item.querySelector("a").attributes["href"],
title: item.querySelector("h3").text,
cover: item.querySelector("img").attributes["src"]
}))
}
return result;
}
// 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: "multiPartPage",
load: async () => {
const res = await Network.get(this.baseUrl, this.headers);
const document = new HtmlDocument(res.body);
const result = [{ title: "近期更新", comics: [], viewMore: null }];
for (let item of document.querySelector(".pb-unit-md").querySelectorAll(".slicarda")) {
result[0].comics.push(new Comic({
id: item.attributes["href"],
title: item.querySelector("h3").text,
cover: item.querySelector("img").attributes["src"]
}))
}
const cardlists = document.querySelectorAll(".cardlist");
const hometitles = document.querySelectorAll(".hometitle");
for (let i = 0; i < hometitles.length; i++) {
result.push({
title: hometitles[i].querySelector("h2").text,
comics: this.parseComics(cardlists[i]),
viewMore: {
page: "category",
attributes: {
category: hometitles[i].querySelector("h2").text,
param: hometitles[i].attributes["href"]
},
}
});
}
return result;
}
}
]
// categories
category = {
/// title of the category page, used to identify the page, it should be unique
title: this.name,
parts: [
{
name: "类型",
type: "fixed",
categories: [
"全部",
"韩漫",
"热门漫画",
"国漫",
"其他",
"日漫",
"欧美"
],
itemType: "category",
categoryParams: [
"/manga",
"/manga-genre/kr",
"/manga-genre/hots",
"/manga-genre/cn",
"/manga-genre/qita",
"/manga-genre/jp",
"/manga-genre/ou-mei"
],
},
{
name: "标签",
type: "fixed",
categories: [
"复仇",
"古风",
"奇幻",
"逆袭",
"异能",
"宅向",
"穿越",
"热血",
"纯爱",
"系统",
"重生",
"冒险",
"灵异",
"大女主",
"剧情",
"恋爱",
"玄幻",
"女神",
"科幻",
"魔幻",
"推理",
"猎奇",
"治愈",
"都市",
"异形",
"青春",
"末日",
"悬疑",
"修仙",
"战斗"
],
itemType: "category",
categoryParams: [
"/manga-tag/fuchou",
"/manga-tag/gufeng",
"/manga-tag/qihuan",
"/manga-tag/nixi",
"/manga-tag/yineng",
"/manga-tag/zhaixiang",
"/manga-tag/chuanyue",
"/manga-tag/rexue",
"/manga-tag/chunai",
"/manga-tag/xitong",
"/manga-tag/zhongsheng",
"/manga-tag/maoxian",
"/manga-tag/lingyi",
"/manga-tag/danvzhu",
"/manga-tag/juqing",
"/manga-tag/lianai",
"/manga-tag/xuanhuan",
"/manga-tag/nvshen",
"/manga-tag/kehuan",
"/manga-tag/mohuan",
"/manga-tag/tuili",
"/manga-tag/lieqi",
"/manga-tag/zhiyu",
"/manga-tag/doushi",
"/manga-tag/yixing",
"/manga-tag/qingchun",
"/manga-tag/mori",
"/manga-tag/xuanyi",
"/manga-tag/xiuxian",
"/manga-tag/zhandou"
],
}
],
// enable ranking page
enableRankingPage: false,
}
/// category comic loading related
categoryComics = {
load: async (category, params, options, page) => {
const res = await Network.get(`${this.baseUrl}${params}/page/${page}`, this.headers);
if (res.status !== 200) {
throw `Invalid status code: ${res.status}`;
}
const document = new HtmlDocument(res.body);
let maxPage = null;
try {
maxPage = parseInt(document.querySelectorAll("button.text-small").pop().text.replaceAll("\n", "").replaceAll(" ", ""));
} catch(_) {
maxPage = 1;
}
return {
comics: this.parseComics(document),
maxPage: maxPage
};
}
}
/// search related
search = {
load: async (keyword, options, page) => {
const res = await Network.get(`${this.baseUrl}/s/${keyword}?page=${page}`);
if (res.status !== 200) {
throw `Invalid status code: ${res.status}`;
}
const document = new HtmlDocument(res.body);
let maxPage = null;
try {
maxPage = parseInt(document.querySelectorAll("button.text-small").pop().text.replaceAll("\n", "").replaceAll(" ", ""));
} catch(_) {
maxPage = 1;
}
return {
comics: this.parseComics(document),
maxPage: maxPage
};
},
// enable tags suggestions
enableTagsSuggestions: false,
}
/// single comic related
comic = {
onThumbnailLoad: (url) => {
return {
headers: this.headers
}
},
loadInfo: async (id) => {
const res = await Network.get(this.baseUrl + id);
if (res.status !== 200) {
throw `Invalid status code: ${res.status}`;
}
const document = new HtmlDocument(res.body);
const title = document.querySelector(".text-xl").text.trim().split(" ")[0]
const cover = document.querySelector(".object-cover").attributes["src"];
const description = document.querySelector("p.text-medium").text;
const infos = document.querySelectorAll("div.py-1");
const tags = { "作者": [], "类型": [], "标签": [] };
for (let author of infos[0].querySelectorAll("a > span")) {
let author_name = author.text.trim();
if (author_name.endsWith(",")) {
author_name = author_name.slice(0, -1).trim();
}
tags["作者"].push(author_name);
}
for (let category of infos[1].querySelectorAll("a > span")) {
let category_name = category.text.trim();
if (category_name.endsWith(",")) {
category_name = category_name.slice(0, -1).trim();
}
tags["类型"].push(category_name);
}
for (let tag of infos[2].querySelectorAll("a")) {
tags["标签"].push(tag.text.replace("\n", "").replaceAll(" ", "").replace("#", ""));
}
const mangaId = document.querySelector("#mangachapters").attributes["data-mid"];
const jsonRes = await Network.get(`${this.apiUrl}/manga/get?mid=${mangaId}&mode=all&t=${Date.now()}`, this.headers);
const jsonData = JSON.parse(jsonRes.body);
const chapters = {};
for (let ch of jsonData["data"]["chapters"]) {
chapters[`${mangaId}@${ch["id"]}`] = ch["attributes"]["title"];
}
const recommend = [];
for (let item of document.querySelectorAll("div.cardlist > div.pb-2")) {
recommend.push(new Comic({
id: item.querySelector("a").attributes["href"],
title: item.querySelector("h3").text,
cover: item.querySelector("img").attributes["src"]
}));
}
return new ComicDetails({
title: title,
cover: cover,
description: description,
tags: tags,
chapters: chapters,
recommend: recommend,
});
},
loadEp: async (comicId, epId) => {
const ids = epId.split("@");
const res = await Network.get(`${this.apiUrl}/chapter/getinfo?m=${ids[0]}&c=${ids[1]}`, this.headers);
if (res.status !== 200) {
throw `Invalid status code: ${res.status}`;
}
const jsonData = JSON.parse(res.body);
const images = [];
for (let i of jsonData["data"]["info"]["images"]["images"]) {
images.push(this.imageUrl + i["url"]);
}
return { images };
},
// enable tags translate
enableTagsTranslate: false,
}
}