Add a new resource from ykmh (#107)

* add new source from comick

* fix some code

* fix gif load and comic list info(none-type/chapter/volume)

* add some comick hidden tags

* revise coding error in file

* info updata time

* fix no-EN error

* add new function

- Multi-language comic selection support
- Added comic recommendations
- Fixed empty chapter return bug
- Resolved tag click issues
- Optimized data processing

* Optimize network request

Remove redundant requests and prevent async deadlocks

* Update comick.js

* new small comic source from baihehui

* Fixed some bugs and added some sorting methods

* Fixed some bugs and added some sorting methods

* Add a new resource from ykmh
This commit is contained in:
Zion
2025-07-23 10:15:26 +08:00
committed by GitHub
parent 891b81ae16
commit afea7a6c99
2 changed files with 889 additions and 0 deletions

883
ykmh.js Normal file
View File

@@ -0,0 +1,883 @@
/** @type {import('./_venera_.js')} */
class YKMHSource extends ComicSource {
name = "优酷漫画"
key = "ykmh"
version = "1.0.0"
minAppVersion = "1.4.0"
url = "https://cdn.jsdelivr.net/gh/venera-app/venera-configs@main/ykmh.js"
get baseUrl() {
return "https://www.ykmh.net";
}
explore = [
{
title: "优酷漫画",
type: "multiPartPage",
load: async (page) => {
let res = await Network.get("https://www.ykmh.net")
if (res.status !== 200) {
throw `Invalid status code: ${res.status}`
}
function parseHotCarousel(html) {
let hotComics = []
let carouselPattern = /<div class="sub-item">\s*<a href="([^"]+)" target="_blank"><img src="([^"]+)" alt="[^"]*"><\/a>\s*<div class="carousel-caption">\s*([^<]+)\s*<\/div>/g
let match
while ((match = carouselPattern.exec(html)) !== null) {
let cover = match[2]
if (!cover.startsWith('http')) {
cover = 'https://www.ykmh.net' + (cover.startsWith('/') ? cover : '/' + cover)
}
hotComics.push(new Comic({
id: match[1],
title: match[3].trim(),
cover: cover,
tags: [`热门推荐`],
description: "热门推荐漫画"
}))
}
if (hotComics.length === 0) {
let keywordPattern = /<li data-key="(\d+)"><a href="(https:\/\/www\.ykmh\.net\/manhua\/[^"]+)"[^>]*>([^<]+)<\/a><\/li>/g
while ((match = keywordPattern.exec(html)) !== null) {
hotComics.push(new Comic({
id: match[2],
title: match[3],
cover: "https://www.ykmh.net/images/default/cover.png",
tags: [`热门关键词`],
description: ""
}))
}
}
return hotComics.slice(0, 10)
}
function parseLatestComics(html) {
let latestComics = []
let comicPattern = /<li data-key="(\d+)"><a class="image-link" href="([^"]+)" title="([^"]+)"><img src="([^"]+)"[^>]*><span class="tip"><p>([^<]*)<\/p><\/span><\/a><p><a href="[^"]*" title="[^"]*">([^<]+)<\/a><\/p>/g
let match
while ((match = comicPattern.exec(html)) !== null) {
let cover = match[4]
if (!cover.startsWith('http')) {
cover = 'https://www.ykmh.net' + (cover.startsWith('/') ? cover : '/' + cover)
}
latestComics.push(new Comic({
id: match[2],
title: match[3],
cover: cover,
tags: [match[5]],
description: `更新至:${match[5]}`
}))
}
return latestComics.slice(0, 15)
}
let hotComics = parseHotCarousel(res.body)
let latestComics = parseLatestComics(res.body)
return [
{
title: "热门推荐",
comics: hotComics
},
{
title: "最新更新",
comics: latestComics
}
]
}
}
]
static category_param_dict = {
"全部": "",
"爱情": "aiqing",
"剧情": "juqing",
"欢乐向": "huanlexiang",
"格斗": "gedou",
"科幻": "kehuan",
"伪娘": "weiniang",
"节操": "jiecao",
"恐怖": "kongbu",
"悬疑": "xuanyi",
"冒险": "maoxian",
"校园": "xiaoyuan",
"治愈": "zhiyu",
"恋爱": "lianai",
"奇幻": "qihuan",
"热血": "rexue",
"限制级": "xianzhiji",
"魔法": "mofa",
"后宫": "hougong",
"魔幻": "mohuan",
"轻小说": "qingxiaoshuo",
"震撼": "zhenhan",
"纯爱": "chunai",
"少女": "shaonv",
"战争": "zhanzheng",
"武侠": "wuxia",
"搞笑": "gaoxiao",
"神鬼": "shengui",
"竞技": "jingji",
"幻想": "huanxiang",
"神魔": "shenmo",
"灵异": "lingyi",
"百合": "baihe",
"运动": "yundong",
"体育": "tiyu",
"惊悚": "jingsong",
"日常": "richang",
"绅士": "shenshi",
"颜艺": "yanyi",
"生活": "shenghuo",
"四格": "sige",
"萌系": "mengxi",
"都市": "dushi",
"同人": "tongren",
"推理": "tuili",
"耽美": "danmei",
"卖肉": "mairou",
"职场": "zhichang",
"侦探": "zhentan",
"战斗": "zhandou",
"爆笑": "baoxiao",
"总裁": "zongcai",
"美食": "meishi",
"性转换": "xingzhuanhuan",
"励志": "lizhi",
"西方魔幻": "xifangmohuan",
"改编": "gaibian",
"其他": "qita",
"宅系": "zhaixi",
"机战": "jizhan",
"乙女": "yinv",
"秀吉": "xiuji",
"舰娘": "jianniang",
"历史": "lishi",
"猎奇": "lieqi",
"社会": "shehui",
"青春": "qingchun",
"高清单行": "gaoqingdanxing",
"东方": "dongfang",
"橘味": "juwei",
"音乐舞蹈": "yinyuewudao",
"家庭": "jiating",
"ゆり": "unknown",
"彩虹": "caihong",
"少年": "shaonian",
"泡泡": "paopao",
"宫斗": "gongdou",
"动作": "dongzuo",
"青年": "qingnian",
"虐心": "nuexin",
"泛爱": "fanai",
"机甲": "jijia",
"装逼": "zhuangbi",
"#愛情": "aiqing2",
"#長條": "zhangtiao",
"#穿越": "chuanyue",
"#生活": "shenghuo2",
"TS": "TS",
"#耽美": "danmei2",
"#后宫": "hougong2",
"#节操": "jiecao2",
"#轻小说": "qingxiaoshuo2",
"#奇幻": "qihuan2",
"#悬疑": "xuanyi2",
"#校园": "xiaoyuan2",
"#爱情": "aiqing3",
"#百合": "baihe2",
"#长条": "changtiao",
"#冒险": "maoxian2",
"#搞笑": "gaoxiao2",
"#欢乐向": "huanlexiang2",
"#职场": "zhichang2",
"#神鬼": "shengui2",
"#生存": "shengcun",
"#治愈": "zhiyu2",
"#竞技": "jingji2",
"#美食": "meishi2",
"#其他": "qita2",
"#机战": "jizhan2",
"#战争": "zhanzheng2",
"#科幻": "kehuan2",
"#四格": "sige2",
"#武侠": "wuxia2",
"#重生": "zhongsheng",
"#性转换": "xingzhuanhuan2",
"#热血": "rexue2",
"#伪娘": "weiniang2",
"#异世界": "yishijie",
"#萌系": "mengxi2",
"#格斗": "gedou2",
"#励志": "lizhi2",
"#都市": "dushi2",
"#惊悚": "jingsong2",
"#侦探": "zhentan2",
"#舰娘": "jianniang2",
"#音乐舞蹈": "yinyuewudao2",
"#TL": "TL",
"#AA": "AA",
"#转生": "zhuansheng",
"#魔幻": "mohuan2",
"---": "unknown2",
"#彩色": "caise",
"福瑞": "furui",
"#FATE": "FATE",
"西幻": "xihuan",
"#C99": "C99",
"#C101": "C101",
"#历史": "lishi2",
"#C102": "C102",
"#无修正": "wuxiuzheng",
"#C103": "C103",
"#东方": "dongfang2",
"栏目": "lanmu",
"异世界": "yishijie2",
"恶搞": "egao",
"霸总": "bazong",
"古风": "gufeng",
"穿越": "chuanyue2",
"玄幻": "xuanhuan",
"日更": "rigeng",
"吸血": "xixie",
"萝莉": "luoli",
"漫改": "mangai",
"唯美": "weimei",
"宅男腐女": "zhainanfunv",
"老师": "laoshi",
"诱惑": "youhuo",
"杂志": "zazhi",
"脑洞": "naodong",
"其它": "qita3",
"#恐怖": "kongbu2",
"#C105": "C105",
"权谋": "quanmou",
"大陆": "dalu",
"日本": "riben",
"香港": "hongkong",
"台湾": "taiwan",
"欧美": "oumei",
"韩国": "hanguo",
"其它": "qita",
"儿童漫画": "ertong",
"少年漫画": "shaonian",
"少女漫画": "shaonv",
"青年漫画": "qingnian",
"已完结": "wanjie",
"连载中": "lianzai"
}
static comic_status = {
"连载中": "ongoing",
"已完结": "completed",
"暂停": "paused",
"完结": "completed",
"连载": "ongoing",
"休刊": "paused",
"未知状态": "unknown"
}
category = {
title: "优酷漫画",
parts: [
{
name: "主题",
type: "fixed",
categories: Object.keys(YKMHSource.category_param_dict),
itemType: "category",
categoryParams: Object.values(YKMHSource.category_param_dict),
}
],
enableRankingPage: false,
}
categoryComics = {
load: async (category, param, options, page) => {
let sort = "";
temp = options[1].split("-")[0]
if(temp==0){sort=""}else{sort="-"}
sort = sort + options[0].split("-")[0]
let url;
if (param === "" || param === undefined) {
url = `https://www.ykmh.net/list/${sort}/?page=${page}`;
} else {
url = `https://www.ykmh.net/list/${param}/${sort}/${page}/`;
}
let res = await Network.get(url);
if (res.status !== 200) {
throw `Invalid status code: ${res.status}`;
}
function parseComicsList(html) {
let comics = []
let comicPattern = /<li class="list-comic" data-key="(\d+)">\s*<a class="comic_img"\s+href="([^"]+)"><img src="([^"]+)" alt="([^"]*)"[^>]*><\/a>\s*<span class="comic_list_det"[^>]*>\s*<h3><a href="[^"]*">([^<]+)<\/a><\/h3>/g
let match
while ((match = comicPattern.exec(html)) !== null) {
let cover = match[3]
if (!cover.startsWith('http')) {
cover = 'https://www.ykmh.net' + (cover.startsWith('/') ? cover : '/' + cover)
}
comics.push(new Comic({
id: match[2],
title: match[5] || match[4],
cover: cover,
tags: [],
description: ""
}))
}
return comics
}
let comics = parseComicsList(res.body)
let maxPage = 1
let pagePattern = /<li class="last"><a href="[^"]*\/(\d+)\/" data-page="\d+">尾页<\/a><\/li>/
let pageMatch = res.body.match(pagePattern)
if (pageMatch) {
maxPage = parseInt(pageMatch[1])
} else {
let altPagePattern = /<li class="last"><a href="[^"]*" data-page="(\d+)">尾页<\/a><\/li>/
let altMatch = res.body.match(altPagePattern)
if (altMatch) {
maxPage = parseInt(altMatch[1]) + 1
}
}
return {
comics: comics,
maxPage: maxPage
};
},
optionList: [
{options:[
"update-更新时间",
"post-发布时间",
"click-点击量"
]},
{options:[
"0-降序",
"1-升序"
]},
]
}
/// search related
search = {
load: async (keyword, options, page) => {
let encodedKeyword = encodeURIComponent(keyword);
let url;
if (page && page > 1) {
url = `https://www.ykmh.net/search/?keywords=${encodedKeyword}&page=${page}`;
} else {
url = `https://www.ykmh.net/search/?keywords=${encodedKeyword}`;
}
let res = await Network.get(url);
if (res.status !== 200) {
throw `Request Error: ${res.status}`;
}
function parseSearchResults(html) {
let comics = []
let comicPattern = /<li class="list-comic" data-key="(\d+)"><a class="image-link"\s+href="([^"]+)"\s+title="([^"]+)"><img src="([^"]+)"[^>]*><\/a>\s*<p><a href="[^"]*"[^>]*>([^<]+)<\/a><\/p>\s*<p class="auth"><a href="[^"]*">([^<]*)<\/a><\/p>\s*<p class="newPage">([^<]*)<\/p>/g
let match
while ((match = comicPattern.exec(html)) !== null) {
let cover = match[4]
if (!cover.startsWith('http')) {
cover = 'https://www.ykmh.net' + (cover.startsWith('/') ? cover : '/' + cover)
}
comics.push(new Comic({
id: match[2],
title: match[3],
cover: cover,
tags: [match[6] || "未知作者", match[7] || ""],
description: `作者:${match[6] || "未知作者"} | 更新至:${match[7] || "未知"}`
}))
}
return comics
}
let comics = parseSearchResults(res.body)
let maxPage = 1
let pagePattern = /<li class="last"><a href="[^"]*page=(\d+)"[^>]*>尾页<\/a><\/li>/
let pageMatch = res.body.match(pagePattern)
if (pageMatch) {
maxPage = parseInt(pageMatch[1])
} else {
let altPagePattern = /<a href="[^"]*page=(\d+)"[^>]*>\s*(\d+)\s*<\/a>/g
let matches = [...res.body.matchAll(altPagePattern)]
if (matches.length > 0) {
maxPage = Math.max(...matches.map(m => parseInt(m[2])))
}
}
return {
comics: comics,
maxPage: maxPage
};
},
optionList: []
}
/// single comic related
comic = {
id: null,
buildId: null,
loadInfo: async (id) => {
if (!id || typeof id !== 'string') {
throw "ID不能为空";
}
let targetUrl = id;
if (id.startsWith('https://www.ykmh.net/')) {
targetUrl = id.replace('https://www.ykmh.net/', 'https://m.ykmh.net/');
}
else if (id.startsWith('/')) {
targetUrl = 'https://m.ykmh.net' + id;
}
else {
targetUrl = 'https://m.ykmh.net/' + id;
}
if (!targetUrl.endsWith('/')) {
targetUrl += '/';
}
let res = await Network.get(targetUrl, {
headers: {
'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36 Edg/139.0.0.0'
}
});
if (res.status !== 200) {
throw `请求失败,状态码: ${res.status}URL: ${targetUrl}`;
}
function parseComicInfo(html) {
try {
if (!html || typeof html !== 'string') {
return {
title: "未知标题",
cover: "https://m.ykmh.net/images/default/cover.png",
author: "未知作者",
status: "未知状态",
tags: [],
description: "暂无描述"
};
}
let title = "未知标题";
try {
let titleMatch = html.match(/<div class="BarTit" id="comicName">([^<]+)<\/div>/);
if (titleMatch && titleMatch[1]) {
title = titleMatch[1].trim();
}
} catch (e) {
console.warn("解析标题失败:", e);
}
let cover = "https://m.ykmh.net/images/default/cover.png";
try {
let coverMatch = html.match(/<div class="pic" id="Cover">\s*<mip-img src="([^"]+)"/);
if (coverMatch && coverMatch[1]) {
cover = coverMatch[1];
} else {
let backupMatch = html.match(/<mip-img src="([^"]+)"/);
if (backupMatch && backupMatch[1]) {
cover = backupMatch[1];
}
}
} catch (e) {
console.warn("解析封面失败:", e);
}
let author = "未知作者";
try {
let authorMatch = html.match(/<p class="txtItme">\s*<span class="icon icon01"><\/span>\s*<a href="[^"]*">([^<]+)<\/a>\s*<\/p>/);
if (authorMatch && authorMatch[1]) {
author = authorMatch[1].trim();
} else {
let authorMatch2 = html.match(/<span class="icon icon01"><\/span>\s*<a href="[^"]*">([^<]+)<\/a>/);
if (authorMatch2 && authorMatch2[1]) {
author = authorMatch2[1].trim();
}
}
console.log("解析到作者:", author);
} catch (e) {
console.warn("解析作者失败:", e);
}
let status = "未知状态";
let tags = [];
try {
let txtItems = html.match(/<p class="txtItme">[\s\S]*?<\/p>/g);
if (txtItems) {
console.log("找到txtItme元素数量:", txtItems.length);
for (let item of txtItems) {
if (item.includes('icon icon02')) {
let tagMatches = item.matchAll(/<a href="[^"]*\/list\/[^"]*\/">([^<]+)<\/a>/g);
if (tagMatches) {
for (let tagMatch of tagMatches) {
if (tagMatch && tagMatch[1]) {
let tagText = tagMatch[1].trim();
if (tagText && !tags.includes(tagText)) {
tags.push(tagText);
if (tagText === '连载中' || tagText === '已完结' || tagText === '完结' ||
tagText === '连载' || tagText === '暂停' || tagText === '休刊') {
status = tagText;
console.log("找到状态标签:", tagText);
}
}
}
}
}
}
}
}
console.log("解析到状态:", status, "标签:", tags);
} catch (e) {
console.warn("解析标签失败:", e);
tags = [];
}
let description = "暂无描述";
try {
let descMatch = html.match(/<mip-showmore[^>]*id="showmore-des">\s*([^<]+(?:<[^>]+>[^<]*<\/[^>]+>[^<]*)*?)\s*<\/mip-showmore>/);
if (descMatch && descMatch[1]) {
description = descMatch[1].replace(/<[^>]+>/g, '').replace(/^\s*介绍:\s*/, '').trim();
}
} catch (e) {
console.warn("解析描述失败:", e);
}
return {
title: title || "未知标题",
cover: cover || "https://m.ykmh.net/images/default/cover.png",
author: author || "未知作者",
status: status || "未知状态",
tags: tags || [],
description: description || "暂无描述"
};
} catch (error) {
console.error("parseComicInfo 总体错误:", error);
return {
title: "未知标题",
cover: "https://m.ykmh.net/images/default/cover.png",
author: "未知作者",
status: "未知状态",
tags: [],
description: "暂无描述"
};
}
}
function parseChapters(html) {
let allChaptersMap = new Map();
let groupedChapters = new Map();
try {
if (!html || typeof html !== 'string') {
return allChaptersMap;
}
let chapterGroupsPattern = /<div class="comic-chapters">[\s\S]*?<span class="Title">([^<]+)<\/span>[\s\S]*?<ul id="chapter-list-(\d+)"[^>]*>([\s\S]*?)<\/ul>/g;
let groupMatch;
while ((groupMatch = chapterGroupsPattern.exec(html)) !== null) {
try {
if (groupMatch && groupMatch[1] && groupMatch[3]) {
let groupTitle = groupMatch[1].trim();
let groupContent = groupMatch[3];
let groupChapters = new Map();
let chapterPattern = /<li>\s*<a href="([^"]+)"[^>]*>\s*<span>([^<]+)<\/span>\s*<\/a>\s*<\/li>/g;
let chapterMatch;
while ((chapterMatch = chapterPattern.exec(groupContent)) !== null) {
try {
if (chapterMatch && chapterMatch[1] && chapterMatch[2]) {
let chapterUrl = chapterMatch[1];
let chapterTitle = chapterMatch[2].trim();
if (!chapterUrl.startsWith('http')) {
chapterUrl = 'https://m.ykmh.net' + (chapterUrl.startsWith('/') ? chapterUrl : '/' + chapterUrl);
}
groupChapters.set(chapterUrl, chapterTitle);
let finalChapterTitle = chapterTitle;
if (groupTitle !== "连载列表") {
finalChapterTitle = `[${groupTitle}] ${chapterTitle}`;
}
allChaptersMap.set(chapterUrl, finalChapterTitle);
}
} catch (e) {
console.warn("解析单个章节失败:", e);
continue;
}
}
if (groupChapters.size > 0) {
groupedChapters.set(groupTitle, groupChapters);
}
}
} catch (e) {
console.warn("解析章节组别失败:", e);
continue;
}
}
if (allChaptersMap.size === 0) {
console.warn("使用备用章节解析方法");
let allChapterPattern = /<li>\s*<a href="([^"]+)"[^>]*>\s*<span>([^<]+)<\/span>\s*<\/a>\s*<\/li>/g;
let match;
while ((match = allChapterPattern.exec(html)) !== null) {
try {
if (match && match[1] && match[2]) {
let chapterUrl = match[1];
let chapterTitle = match[2].trim();
if (!chapterUrl.startsWith('http')) {
chapterUrl = 'https://m.ykmh.net' + (chapterUrl.startsWith('/') ? chapterUrl : '/' + chapterUrl);
}
allChaptersMap.set(chapterUrl, chapterTitle);
}
} catch (e) {
console.warn("解析单个章节失败:", e);
continue;
}
}
}
if (groupedChapters.size > 1) {
console.log("使用多分组模式");
return groupedChapters;
}
console.log("使用合并模式");
return allChaptersMap;
} catch (error) {
console.error("parseChapters 总体错误:", error);
}
return allChaptersMap;
}
function parseRecommends(html) {
let recommends = [];
try {
if (!html || typeof html !== 'string') {
return recommends;
}
let recommendPattern = /<li class="list-comic" data-key="[^"]*">\s*<a class="ImgA" href="([^"]+)"><mip-img src="([^"]+)"[^>]*alt="([^"]*)"[^>]*><\/mip-img><\/a>\s*<a class="txtA" href="[^"]+">([^<]+)<\/a>/g;
let match;
let count = 0;
while ((match = recommendPattern.exec(html)) !== null && count < 10) {
try {
if (match && match[1] && match[2]) {
let recUrl = match[1];
let recCover = match[2];
let recTitle = (match[4] && match[4].trim()) || (match[3] && match[3].trim()) || "未知标题";
recommends.push(new Comic({
id: recUrl,
title: recTitle,
cover: recCover
}));
count++;
}
} catch (e) {
console.warn("解析单个推荐漫画失败:", e);
continue;
}
}
} catch (error) {
console.error("parseRecommends 总体错误:", error);
}
return recommends;
}
try {
this.comic.id = id;
if (!res.body || typeof res.body !== 'string') {
throw "响应内容为空或格式错误";
}
let comicInfo = parseComicInfo(res.body);
let chapters = parseChapters(res.body);
let recommends = parseRecommends(res.body);
comicInfo = comicInfo || {};
if (!comicInfo.title) comicInfo.title = "未知标题";
if (!comicInfo.author) comicInfo.author = "未知作者";
if (!comicInfo.status) comicInfo.status = "未知状态";
if (!comicInfo.description) comicInfo.description = "暂无描述";
if (!comicInfo.cover) comicInfo.cover = "https://m.ykmh.net/images/default/cover.png";
if (!comicInfo.tags || !Array.isArray(comicInfo.tags)) comicInfo.tags = [];
let updateInfo = "暂无更新";
try {
if (chapters && chapters.size > 0) {
let latestChapter = null;
let firstValue = Array.from(chapters.values())[0];
if (firstValue instanceof Map) {
for (let groupChapters of chapters.values()) {
if (groupChapters.size > 0) {
latestChapter = Array.from(groupChapters.values())[0];
break;
}
}
} else {
latestChapter = firstValue;
}
if (latestChapter) {
updateInfo = `更新至:${latestChapter}`;
}
}
} catch (e) {
console.warn("获取更新信息失败:", e);
}
let mappedStatus = comicInfo.status;
try {
if (YKMHSource.comic_status && comicInfo.status) {
mappedStatus = YKMHSource.comic_status[comicInfo.status];
if (!mappedStatus) {
if (comicInfo.status.includes('连载')) {
mappedStatus = "ongoing";
} else if (comicInfo.status.includes('完结')) {
mappedStatus = "completed";
} else if (comicInfo.status.includes('暂停') || comicInfo.status.includes('休刊')) {
mappedStatus = "paused";
} else {
mappedStatus = comicInfo.status; // 保持原状态
}
}
}
} catch (e) {
console.warn("状态映射失败:", e);
mappedStatus = comicInfo.status;
}
return {
title: comicInfo.title,
cover: comicInfo.cover,
description: comicInfo.description,
tags: {
"作者": [comicInfo.author],
"状态": [mappedStatus],
"更新": [updateInfo],
"标签": comicInfo.tags
},
chapters: chapters || new Map(),
recommend: recommends || []
};
} catch (error) {
console.error("loadInfo详细错误:", error);
throw `解析漫画信息失败: ${error.message || error}`;
}
},
loadEp: async (comicId, epId) => {
if (!comicId || typeof comicId !== 'string') {
throw "漫画ID不能为空";
}
if (!epId || typeof epId !== 'string') {
throw "章节ID不能为空";
}
let chapterUrl = epId;
if (!chapterUrl.startsWith('http')) {
chapterUrl = 'https://m.ykmh.net' + (chapterUrl.startsWith('/') ? chapterUrl : '/' + chapterUrl);
} else if (chapterUrl.startsWith('https://www.ykmh.net/')) {
chapterUrl = chapterUrl.replace('https://www.ykmh.net/', 'https://m.ykmh.net/');
}
let res = await Network.get(chapterUrl, {
headers: {
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1'
}
});
if (res.status !== 200) {
throw `请求章节失败,状态码: ${res.status}`;
}
function parseChapterImages(html) {
let images = [];
let scriptMatch = html.match(/var\s+chapterImages\s*=\s*(\[.*?\]);/);
if (scriptMatch) {
try {
let imageList = JSON.parse(scriptMatch[1]);
images = imageList.map(img => {
if (!img.startsWith('http')) {
return 'https://m.ykmh.net' + (img.startsWith('/') ? img : '/' + img);
}
return img;
});
} catch (e) {
}
}
if (images.length === 0) {
let imgPattern = /<img[^>]+src="([^"]+)"[^>]*>/g;
let match;
while ((match = imgPattern.exec(html)) !== null) {
let imgSrc = match[1];
if (imgSrc.includes('cover') || imgSrc.includes('avatar') ||
imgSrc.includes('logo') || imgSrc.includes('icon') ||
imgSrc.includes('banner')) {
continue;
}
if (!imgSrc.startsWith('http')) {
imgSrc = 'https://m.ykmh.net' + (imgSrc.startsWith('/') ? imgSrc : '/' + imgSrc);
}
images.push(imgSrc);
}
}
if (images.length === 0) {
let containerPattern = /<div[^>]*class="[^"]*chapter[^"]*"[^>]*>(.*?)<\/div>/gs;
let containerMatch = html.match(containerPattern);
if (containerMatch) {
let containerHtml = containerMatch[1];
let imgPattern = /<img[^>]+src="([^"]+)"/g;
let match;
while ((match = imgPattern.exec(containerHtml)) !== null) {
let imgSrc = match[1];
if (!imgSrc.startsWith('http')) {
imgSrc = 'https://m.ykmh.net' + (imgSrc.startsWith('/') ? imgSrc : '/' + imgSrc);
}
images.push(imgSrc);
}
}
}
return images;
}
try {
let images = parseChapterImages(res.body);
if (images.length === 0) {
console.warn("未找到章节图片,可能需要进一步的页面解析");
}
return { images: images || [] };
} catch (error) {
throw `解析章节图片失败: ${error.message || error}`;
}
},
onClickTag: (namespace, tag) => {
if (namespace === "标签") {
let r_tag = YKMHSource.category_param_dict[tag];
return {
action: 'category',
keyword: `${tag}`,
param: `${r_tag}`,
}
}
throw "未支持此类Tag检索"
}
}
}