mirror of
https://github.com/venera-app/venera-configs.git
synced 2025-12-16 17:31:16 +00:00
Compare commits
7 Commits
21251d7a77
...
0d3be98981
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d3be98981 | ||
|
|
c193ea652f | ||
|
|
4864f0bf3b | ||
|
|
e18068fd8a | ||
|
|
848809a645 | ||
|
|
c9d921489a | ||
|
|
efb62f5fd6 |
@@ -25,7 +25,7 @@ class NewComicSource extends ComicSource {
|
|||||||
|
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
|
||||||
minAppVersion = "1.4.0"
|
minAppVersion = "1.6.0"
|
||||||
|
|
||||||
// update url
|
// update url
|
||||||
url = ""
|
url = ""
|
||||||
@@ -73,7 +73,8 @@ class NewComicSource extends ComicSource {
|
|||||||
loginWithWebview: {
|
loginWithWebview: {
|
||||||
url: "",
|
url: "",
|
||||||
/**
|
/**
|
||||||
* check login status
|
* check login status.
|
||||||
|
* After successful login, the cookie will be automatically saved, and the localstorage can be retrieved using this.loadData("_localStorage").
|
||||||
* @param url {string} - current url
|
* @param url {string} - current url
|
||||||
* @param title {string} - current title
|
* @param title {string} - current title
|
||||||
* @returns {boolean} - return true if login success
|
* @returns {boolean} - return true if login success
|
||||||
@@ -819,4 +820,4 @@ class NewComicSource extends ComicSource {
|
|||||||
'zh_TW': {},
|
'zh_TW': {},
|
||||||
'en': {}
|
'en': {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1334,7 +1334,7 @@ let UI = {
|
|||||||
* Show an input dialog
|
* Show an input dialog
|
||||||
* @param title {string}
|
* @param title {string}
|
||||||
* @param validator {(string) => string | null | undefined} - A function that validates the input. If the function returns a string, the dialog will show the error message.
|
* @param validator {(string) => string | null | undefined} - A function that validates the input. If the function returns a string, the dialog will show the error message.
|
||||||
* @param image {string?} - Available since 1.4.6. An optional image to show in the dialog. You can use this to show a captcha.
|
* @param image {string | ArrayBuffer | null | undefined} - Since 1.4.6, you can pass an image url to show an image in the dialog. Since 1.5.3, you can also pass an ArrayBuffer to show a custom image.
|
||||||
* @returns {Promise<string | null>} - The input value. If the dialog is canceled, return null.
|
* @returns {Promise<string | null>} - The input value. If the dialog is canceled, return null.
|
||||||
*/
|
*/
|
||||||
showInputDialog: (title, validator, image) => {
|
showInputDialog: (title, validator, image) => {
|
||||||
@@ -1403,7 +1403,7 @@ let APP = {
|
|||||||
* Set clipboard text
|
* Set clipboard text
|
||||||
* @param text {string}
|
* @param text {string}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*
|
*
|
||||||
* @since 1.3.4
|
* @since 1.3.4
|
||||||
*/
|
*/
|
||||||
function setClipboard(text) {
|
function setClipboard(text) {
|
||||||
@@ -1416,7 +1416,7 @@ function setClipboard(text) {
|
|||||||
/**
|
/**
|
||||||
* Get clipboard text
|
* Get clipboard text
|
||||||
* @returns {Promise<string>}
|
* @returns {Promise<string>}
|
||||||
*
|
*
|
||||||
* @since 1.3.4
|
* @since 1.3.4
|
||||||
*/
|
*/
|
||||||
function getClipboard() {
|
function getClipboard() {
|
||||||
|
|||||||
365
comic_walker.js
Normal file
365
comic_walker.js
Normal file
@@ -0,0 +1,365 @@
|
|||||||
|
class ComicWalker extends ComicSource {
|
||||||
|
name = "カドコミ";
|
||||||
|
key = "comic_walker";
|
||||||
|
version = "1.0.0";
|
||||||
|
minAppVersion = "1.6.0";
|
||||||
|
url =
|
||||||
|
"https://git.nyne.dev/nyne/venera-configs/raw/branch/main/comic_walker.js";
|
||||||
|
|
||||||
|
api_key = "ytBrdQ2ZYdRQguqEusVLxQVUgakNnVht";
|
||||||
|
|
||||||
|
latestVersion = "1.4.13";
|
||||||
|
|
||||||
|
api_base = "https://mobileapp.comic-walker.com";
|
||||||
|
|
||||||
|
get headers() {
|
||||||
|
const headers = {
|
||||||
|
"X-API-Environment-Key": this.api_key,
|
||||||
|
"User-Agent": `BookWalkerApp/${this.latestVersion} (Android 13)`,
|
||||||
|
"Host": "mobileapp.comic-walker.com",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
};
|
||||||
|
const token = this.loadData("token");
|
||||||
|
if (token) {
|
||||||
|
headers["Authorization"] = `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
async refreshToken() {
|
||||||
|
const res = await this.request(
|
||||||
|
`${this.api_base}/v1/users`,
|
||||||
|
this.headers,
|
||||||
|
"POST",
|
||||||
|
);
|
||||||
|
|
||||||
|
this.saveData("token", res.resources.access_token);
|
||||||
|
return res.resources.access_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
async request(url, headers, method = "GET", data) {
|
||||||
|
let response;
|
||||||
|
if (method === "GET") {
|
||||||
|
response = await Network.get(url, headers);
|
||||||
|
} else if (method === "POST") {
|
||||||
|
response = await Network.post(url, headers, data);
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unsupported method: ${method}`);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
response.status === 204
|
||||||
|
) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
response = JSON.parse(response.body);
|
||||||
|
if (
|
||||||
|
response.code === "invalid_request_parameter" ||
|
||||||
|
response.code === "free_daily_reward_quota_exceeded" ||
|
||||||
|
response.code === "unauthorized"
|
||||||
|
) {
|
||||||
|
await this.refreshToken();
|
||||||
|
if (method === "GET") {
|
||||||
|
response = await Network.get(url, this.headers);
|
||||||
|
} else if (method === "POST") {
|
||||||
|
response = await Network.post(url, this.headers, data);
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unsupported method: ${method}`);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
response.status === 204
|
||||||
|
) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
response = JSON.parse(response.body);
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
const itunes_api = "https://itunes.apple.com/lookup?bundleId=jp.co.bookwalker.cwapp.ios&country=jp";
|
||||||
|
|
||||||
|
const resp = await Network.get(itunes_api);
|
||||||
|
|
||||||
|
if (resp.status == 200) {
|
||||||
|
response = JSON.parse(resp.body);
|
||||||
|
this.latestVersion = response.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.refreshToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
explore = [
|
||||||
|
{
|
||||||
|
title: "カドコミ",
|
||||||
|
type: "singlePageWithMultiPart",
|
||||||
|
load: async () => {
|
||||||
|
const res = await this.request(
|
||||||
|
`${this.api_base}/v2/screens/home`,
|
||||||
|
this.headers,
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = {};
|
||||||
|
|
||||||
|
const newArrivals = res.resources.new_arrival_comics.map((item) =>
|
||||||
|
new Comic({
|
||||||
|
id: item.id,
|
||||||
|
title: item.title,
|
||||||
|
cover: item.thumbnail_1x1 || "",
|
||||||
|
tags: item.comic_labels?.map((l) => l.name) || [],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
result["今日の更新"] = newArrivals;
|
||||||
|
|
||||||
|
const attention = res.resources.attention_comics.map((item) =>
|
||||||
|
new Comic({
|
||||||
|
id: item.comic_id,
|
||||||
|
title: item.title,
|
||||||
|
cover: item.image_url || "",
|
||||||
|
tags: item.comic_labels?.map((l) => l.name) || [],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
result["注目作品"] = attention;
|
||||||
|
|
||||||
|
for (const pickup of res.resources.pickup_comics) {
|
||||||
|
const comics = pickup.comics.map((item) =>
|
||||||
|
new Comic({
|
||||||
|
id: item.id,
|
||||||
|
title: item.title,
|
||||||
|
cover: item.thumbnail_1x1 || "",
|
||||||
|
tags: item.comic_labels?.map((l) => l.name) || [],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
result[pickup.name] = comics;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newSerialization = res.resources.new_serialization_comics.map((item) =>
|
||||||
|
new Comic({
|
||||||
|
id: item.id,
|
||||||
|
title: item.title,
|
||||||
|
cover: item.thumbnail_1x1 || "",
|
||||||
|
tags: item.comic_labels?.map((l) => l.name) || [],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
result["新連載"] = newSerialization;
|
||||||
|
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
search = {
|
||||||
|
load: async (keyword, _, page) => {
|
||||||
|
const res = await this.request(
|
||||||
|
`${this.api_base}/v1/search/comics?keyword=${keyword}&limit=20&offset=${
|
||||||
|
(page - 1) * 20
|
||||||
|
}`,
|
||||||
|
this.headers,
|
||||||
|
);
|
||||||
|
|
||||||
|
const comics = res.resources.map((item) =>
|
||||||
|
new Comic({
|
||||||
|
id: item.id,
|
||||||
|
title: item.title,
|
||||||
|
cover: item.thumbnail_1x1 || "",
|
||||||
|
tags: [
|
||||||
|
...(item.authors?.map((a) => a.name) || []),
|
||||||
|
...(item.comic_labels?.map((l) => l.name) || []),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const pageInfo = {
|
||||||
|
hasNextPage: res.resources.length === 20,
|
||||||
|
endCursor: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
comics,
|
||||||
|
maxPage: pageInfo.hasNextPage ? (page || 1) + 1 : (page || 1),
|
||||||
|
endCursor: pageInfo.endCursor,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
comic = {
|
||||||
|
loadInfo: async (id) => {
|
||||||
|
const res = await this.request(
|
||||||
|
`${this.api_base}/v2/screens/comics/${id}`,
|
||||||
|
this.headers,
|
||||||
|
);
|
||||||
|
const detail = res.resources.detail;
|
||||||
|
|
||||||
|
const totalCount = res.resources.episode_total_count || 0;
|
||||||
|
let episodes = { resources: [] };
|
||||||
|
for (let offset = 0; offset < totalCount; offset += 100) {
|
||||||
|
const chunk = await this.request(
|
||||||
|
`${this.api_base}/v1/comics/${id}/episodes?offset=${offset}&limit=100&sort=asc`,
|
||||||
|
this.headers,
|
||||||
|
);
|
||||||
|
episodes.resources.push(...(chunk.resources || []));
|
||||||
|
}
|
||||||
|
|
||||||
|
const tags = new Map();
|
||||||
|
|
||||||
|
if (detail.authors) {
|
||||||
|
detail.authors.forEach((a) => {
|
||||||
|
if (!tags.has(a.role)) tags.set(a.role, []);
|
||||||
|
tags.get(a.role).push(a.name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (detail.comic_labels) {
|
||||||
|
detail.comic_labels.forEach((l) => {
|
||||||
|
if (!tags.has("Labels")) tags.set("Labels", []);
|
||||||
|
tags.get("Labels").push(l.name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (detail.tags) {
|
||||||
|
detail.tags.forEach((t) => {
|
||||||
|
if (!tags.has(t.type)) tags.set(t.type, []);
|
||||||
|
tags.get(t.type).push(t.name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const chapters = new Map();
|
||||||
|
for (const ep of episodes.resources) {
|
||||||
|
let canRent = false;
|
||||||
|
const plans = (ep.plans || []).filter((plan) =>
|
||||||
|
plan.type !== "paid"
|
||||||
|
);
|
||||||
|
if (Array.isArray(plans) && plans.length > 0) {
|
||||||
|
canRent = true;
|
||||||
|
}
|
||||||
|
const title = canRent ? ep.title : `❌ ${ep.title}`;
|
||||||
|
chapters.set(ep.id, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ComicDetails({
|
||||||
|
title: detail.title,
|
||||||
|
subtitle: detail.authors?.map((a) => a.name).join("・") || "",
|
||||||
|
cover: detail.thumbnail_1x1 || "",
|
||||||
|
description: detail.story?.replace(/<br\s*\/?>/gi, "\n") || "",
|
||||||
|
tags,
|
||||||
|
chapters,
|
||||||
|
updateTime: detail.next_update_at,
|
||||||
|
url: detail.share_url,
|
||||||
|
maxPage: totalCount,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
loadEp: async (comicId, epId) => {
|
||||||
|
let detail = await this.request(
|
||||||
|
`${this.api_base}/v1/episodes/${epId}`,
|
||||||
|
this.headers,
|
||||||
|
);
|
||||||
|
const plans = (detail.plans || []).filter((plan) =>
|
||||||
|
// plan.type !== "daily_video_free" &&
|
||||||
|
plan.type !== "paid"
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
!Array.isArray(plans) ||
|
||||||
|
plans.length === 0
|
||||||
|
) {
|
||||||
|
throw new Error("No available rental plans after filtering");
|
||||||
|
}
|
||||||
|
console.log(plans);
|
||||||
|
const freePlan = plans.find((plan) => plan.type === "free");
|
||||||
|
if (!freePlan) {
|
||||||
|
const plan = plans[randomInt(0, plans.length - 1)];
|
||||||
|
await this.request(
|
||||||
|
`${this.api_base}/v1/users/me/rental_episodes`,
|
||||||
|
this.headers,
|
||||||
|
"POST",
|
||||||
|
{ episode_id: epId, reading_method: plan.type },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let res = await this.request(
|
||||||
|
`${this.api_base}/v1/screens/comics/${comicId}/episodes/${epId}/viewer`,
|
||||||
|
this.headers,
|
||||||
|
);
|
||||||
|
const manuscripts = res.resources.manuscripts || [];
|
||||||
|
return {
|
||||||
|
images: manuscripts.map((m) =>
|
||||||
|
`${m.drm_image_url}&drm_hash=${m.drm_hash}`
|
||||||
|
),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
onImageLoad: (url) => {
|
||||||
|
let drm_hash = null;
|
||||||
|
let cleanUrl = url;
|
||||||
|
const drmHashMatch = url.match(/[?&]drm_hash=([^&]+)/);
|
||||||
|
if (drmHashMatch) {
|
||||||
|
drm_hash = decodeURIComponent(drmHashMatch[1]);
|
||||||
|
cleanUrl = url.replace(/([?&])drm_hash=[^&]+(&)?/, (match, p1, p2) => {
|
||||||
|
if (p2) return p1;
|
||||||
|
return "";
|
||||||
|
}).replace(/[?&]$/, "");
|
||||||
|
}
|
||||||
|
cleanUrl = cleanUrl.replace(/([?&])weight=[^&]+(&)?/, (match, p1, p2) => {
|
||||||
|
if (p2) return p1;
|
||||||
|
return "";
|
||||||
|
}).replace(/[?&]$/, "");
|
||||||
|
|
||||||
|
cleanUrl = cleanUrl.replace(/([?&])height=[^&]+(&)?/, (match, p1, p2) => {
|
||||||
|
if (p2) return p1;
|
||||||
|
return "";
|
||||||
|
}).replace(/[?&]$/, "");
|
||||||
|
|
||||||
|
if (drm_hash.length < 2) {
|
||||||
|
throw new Error(
|
||||||
|
"drm_hash must be at least 2 characters long",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
var version = drm_hash.slice(0, 2);
|
||||||
|
if (version !== "01") {
|
||||||
|
throw new Error("Unsupported version: " + version);
|
||||||
|
}
|
||||||
|
var key_part = drm_hash.slice(2);
|
||||||
|
if (key_part.length < 16) {
|
||||||
|
throw new Error(
|
||||||
|
"Key part must be 16 characters long (8 hex numbers)",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
var key = [];
|
||||||
|
for (var i = 0; i < 8; i++) {
|
||||||
|
key.push(parseInt(key_part.slice(i * 2, i * 2 + 2), 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyArray = key;
|
||||||
|
const onResponseScript = `
|
||||||
|
function onResponse(buffer) {
|
||||||
|
var key = [${keyArray.join(',')}];
|
||||||
|
var view = new Uint8Array(buffer);
|
||||||
|
for (var i = 0; i < view.length; i++) {
|
||||||
|
view[i] ^= key[i % key.length];
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
onResponse;
|
||||||
|
`;
|
||||||
|
return {
|
||||||
|
url: cleanUrl,
|
||||||
|
headers: this.headers,
|
||||||
|
onResponse: async (buffer) => {
|
||||||
|
return await compute(onResponseScript, buffer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
onClickTag: (namespace, tag) => {
|
||||||
|
if (
|
||||||
|
namespace === "漫画" || namespace === "原作" ||
|
||||||
|
namespace === "キャラクター原案" || namespace === "著者"
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
action: "search",
|
||||||
|
keyword: tag,
|
||||||
|
param: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw "未支持此类Tag检索";
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -4,9 +4,9 @@ class CopyManga extends ComicSource {
|
|||||||
|
|
||||||
key = "copy_manga"
|
key = "copy_manga"
|
||||||
|
|
||||||
version = "1.3.8"
|
version = "1.4.0"
|
||||||
|
|
||||||
minAppVersion = "1.2.1"
|
minAppVersion = "1.6.0"
|
||||||
|
|
||||||
url = "https://git.nyne.dev/nyne/venera-configs/raw/branch/main/copy_manga.js"
|
url = "https://git.nyne.dev/nyne/venera-configs/raw/branch/main/copy_manga.js"
|
||||||
|
|
||||||
@@ -864,6 +864,62 @@ class CopyManga extends ComicSource {
|
|||||||
return "ok"
|
return "ok"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
loadChapterComments: async (comicId, epId, page, replyTo) => {
|
||||||
|
let url = `${this.apiUrl}/api/v3/roasts?chapter_id=${epId}&limit=20&offset=${(page - 1) * 20}`;
|
||||||
|
let res = await Network.get(
|
||||||
|
url,
|
||||||
|
this.headers,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res.status !== 200) {
|
||||||
|
throw `Invalid status code: ${res.status}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = JSON.parse(res.body);
|
||||||
|
|
||||||
|
let total = data.results.total;
|
||||||
|
|
||||||
|
return {
|
||||||
|
comments: data.results.list.map(e => {
|
||||||
|
return {
|
||||||
|
userName: e.user_name,
|
||||||
|
avatar: e.user_avatar,
|
||||||
|
content: e.comment,
|
||||||
|
time: e.create_at,
|
||||||
|
replyCount: null,
|
||||||
|
id: null,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
maxPage: (total - (total % 20)) / 20 + 1,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sendChapterComment: async (comicId, epId, content, replyTo) => {
|
||||||
|
let token = this.loadData("token");
|
||||||
|
if (!token) {
|
||||||
|
throw "未登录"
|
||||||
|
}
|
||||||
|
let res = await Network.post(
|
||||||
|
`${this.apiUrl}/api/v3/member/roast`,
|
||||||
|
{
|
||||||
|
...this.headers,
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
|
||||||
|
},
|
||||||
|
`chapter_id=${epId}&roast=${encodeURIComponent(content)}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res.status === 401) {
|
||||||
|
throw `Login expired`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.status !== 200) {
|
||||||
|
if(res.status === 210) {
|
||||||
|
throw `210:评论过于频繁或评论内容过短过长`;
|
||||||
|
}
|
||||||
|
throw `Invalid status code: ${res.status}`;
|
||||||
|
} else {
|
||||||
|
return "ok"
|
||||||
|
}
|
||||||
|
},
|
||||||
onClickTag: (namespace, tag) => {
|
onClickTag: (namespace, tag) => {
|
||||||
if (namespace === "标签") {
|
if (namespace === "标签") {
|
||||||
return {
|
return {
|
||||||
|
|||||||
12
index.json
12
index.json
@@ -3,7 +3,7 @@
|
|||||||
"name": "拷贝漫画",
|
"name": "拷贝漫画",
|
||||||
"fileName": "copy_manga.js",
|
"fileName": "copy_manga.js",
|
||||||
"key": "copy_manga",
|
"key": "copy_manga",
|
||||||
"version": "1.3.8"
|
"version": "1.4.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Komiic",
|
"name": "Komiic",
|
||||||
@@ -96,13 +96,13 @@
|
|||||||
"name": "漫画柜",
|
"name": "漫画柜",
|
||||||
"fileName": "manhuagui.js",
|
"fileName": "manhuagui.js",
|
||||||
"key": "ManHuaGui",
|
"key": "ManHuaGui",
|
||||||
"version": "1.1.1"
|
"version": "1.2.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "漫蛙吧",
|
"name": "漫蛙吧",
|
||||||
"fileName": "manwaba.js",
|
"fileName": "manwaba.js",
|
||||||
"key": "manwaba",
|
"key": "manwaba",
|
||||||
"version": "1.0.1"
|
"version": "1.0.2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Lanraragi",
|
"name": "Lanraragi",
|
||||||
@@ -115,5 +115,11 @@
|
|||||||
"fileName": "komga.js",
|
"fileName": "komga.js",
|
||||||
"key": "komga",
|
"key": "komga",
|
||||||
"version": "1.0.0"
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "カドコミ",
|
||||||
|
"fileName": "comic_walker.js",
|
||||||
|
"key": "comic_walker",
|
||||||
|
"version": "1.0.0"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
43
manhuagui.js
43
manhuagui.js
@@ -4,7 +4,7 @@ class ManHuaGui extends ComicSource {
|
|||||||
|
|
||||||
key = "ManHuaGui";
|
key = "ManHuaGui";
|
||||||
|
|
||||||
version = "1.1.1";
|
version = "1.2.0";
|
||||||
|
|
||||||
minAppVersion = "1.4.0";
|
minAppVersion = "1.4.0";
|
||||||
|
|
||||||
@@ -453,6 +453,14 @@ class ManHuaGui extends ComicSource {
|
|||||||
let imgInfos = extractFields(imgData);
|
let imgInfos = extractFields(imgData);
|
||||||
return imgInfos;
|
return imgInfos;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.decodeViewState = function (viewState) {
|
||||||
|
if (!viewState) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let decoded = LZString.decompressFromBase64(viewState);
|
||||||
|
return decoded;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// explore page list
|
// explore page list
|
||||||
@@ -848,6 +856,7 @@ class ManHuaGui extends ComicSource {
|
|||||||
loadInfo: async (id) => {
|
loadInfo: async (id) => {
|
||||||
let url = `${this.baseUrl}/comic/${id}/`;
|
let url = `${this.baseUrl}/comic/${id}/`;
|
||||||
let document = await this.getHtml(url);
|
let document = await this.getHtml(url);
|
||||||
|
|
||||||
// ANCHOR 基本信息
|
// ANCHOR 基本信息
|
||||||
let book = document.querySelector(".book-cont");
|
let book = document.querySelector(".book-cont");
|
||||||
let title = book
|
let title = book
|
||||||
@@ -896,19 +905,45 @@ class ManHuaGui extends ComicSource {
|
|||||||
};
|
};
|
||||||
let updateTime = detail_list[8].text.trim();
|
let updateTime = detail_list[8].text.trim();
|
||||||
|
|
||||||
// ANCHOR 章节信息
|
let chapterDocument = document;
|
||||||
|
let isAdultWarning = document.querySelector("#checkAdult");
|
||||||
|
let viewStateElement = document.querySelector("#__VIEWSTATE");
|
||||||
|
if (isAdultWarning && viewStateElement) {
|
||||||
|
let viewStateValue = viewStateElement.attributes["value"];
|
||||||
|
if (viewStateValue) {
|
||||||
|
let decodedViewState = this.decodeViewState(viewStateValue);
|
||||||
|
if (decodedViewState) {
|
||||||
|
let sanitized = decodedViewState.trim();
|
||||||
|
sanitized = sanitized.replace(/^\/\/+/, "").trim();
|
||||||
|
if (!/class=['"]chapter['"]/.test(sanitized)) {
|
||||||
|
sanitized = `<div class="chapter">${sanitized}</div>`;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
chapterDocument = new HtmlDocument(sanitized);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("解析成人章节列表失败:", error);
|
||||||
|
chapterDocument = document;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 支持多分组
|
// 支持多分组
|
||||||
let chaptersMap = new Map();
|
let chaptersMap = new Map();
|
||||||
|
|
||||||
// 查找所有章节分组标题
|
// 查找所有章节分组标题
|
||||||
let chapterGroups = document.querySelectorAll(".chapter h4 span");
|
let chapterGroups = chapterDocument.querySelectorAll(".chapter h4 span");
|
||||||
|
if (chapterGroups.length === 0) {
|
||||||
|
chapterDocument = document;
|
||||||
|
chapterGroups = chapterDocument.querySelectorAll(".chapter h4 span");
|
||||||
|
}
|
||||||
|
|
||||||
// 处理每个分组
|
// 处理每个分组
|
||||||
for (let i = 0; i < chapterGroups.length; i++) {
|
for (let i = 0; i < chapterGroups.length; i++) {
|
||||||
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 = chapterDocument.querySelectorAll(".chapter-list")[i];
|
||||||
if (chapterList) {
|
if (chapterList) {
|
||||||
let lis = chapterList.querySelectorAll("li");
|
let lis = chapterList.querySelectorAll("li");
|
||||||
for (let li of lis) {
|
for (let li of lis) {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ class ManWaBa extends ComicSource {
|
|||||||
// unique id of the source
|
// unique id of the source
|
||||||
key = "manwaba";
|
key = "manwaba";
|
||||||
|
|
||||||
version = "1.0.1";
|
version = "1.0.2";
|
||||||
|
|
||||||
minAppVersion = "1.4.0";
|
minAppVersion = "1.4.0";
|
||||||
|
|
||||||
@@ -400,7 +400,7 @@ class ManWaBa extends ComicSource {
|
|||||||
let imageRes = await this.fetchJson(imgApi, {
|
let imageRes = await this.fetchJson(imgApi, {
|
||||||
params: {
|
params: {
|
||||||
...params,
|
...params,
|
||||||
pageSize: pageNum,
|
page_size: pageNum,
|
||||||
},
|
},
|
||||||
}).then((res) => res.data.images);
|
}).then((res) => res.data.images);
|
||||||
let images = imageRes.map((item) => item.url);
|
let images = imageRes.map((item) => item.url);
|
||||||
|
|||||||
Reference in New Issue
Block a user