墨云阁
https://moyun.io
zpccool (13551) 16小时前 下载:314
小说
墨云阁小说网 - 免费无弹窗小说阅读
// @name 墨云阁
// @uuid moyunge
// @version 1.0.0
// @author AI
// @url https://moyun.io
// @type novel
// @enabled true
// @description 墨云阁小说网 - 免费无弹窗小说阅读
var BASE = 'https://moyun.io';
var CF_COOKIE = '__51cke__=; hidecta=no; __tins__21985711=%7B%22sid%22%3A%201777916367974%2C%20%22vd%22%3A%201%2C%20%22expires%22%3A%201777918167974%7D; __51laig__=6; cf_clearance=AkqgXhKpwXkQUcQFBugGQBLsAJSgAxZ2CHNzRoIgrZg-1777917010-1.2.1.1-jLtUvVnKLfCXC.M7Fkeg8NCpN4SjMQfC7ZXApuxPT30KJC_gXeVmtGLl2Bfmsoncw.PAkDOlBEnbDLye_7GQBbRVxl1f2YDvqEbZ7IsLA5SPNmBP_hUbdLzMJIHJ20cvjTc4yoVpZ5VJkI8_KAaTlWDKiRGcI.S7QbJ6NUqEXEseJNqpbB.Vhq7OwfDOSyaglyNZ2eYZtFAdj6RPoI7U_adCqpq5uO3Y7ihSF6ElAE6yGiTO4WluVgPDDhjS9bNTSQqiVln2sCSTyCOsqM3N7vKiLAUf6Znogp0t9uRPovOd2o_uIIdQ5NjAniRTU0L6OyYxgLK70ByiT8EY_cSVOA';
var UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36';
var DEFAULT_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Cache-Control': 'no-cache',
'Pragma': 'no-cache',
'Sec-Ch-Ua': '"Google Chrome";v="147", "Not.A/Brand";v="8", "Chromium";v="147"',
'Sec-Ch-Ua-Mobile': '?0',
'Sec-Ch-Ua-Platform': '"Windows"',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-User': '?1',
'Upgrade-Insecure-Requests': '1'
};
var GENRE_MAP = {
'玄幻': 'xuanhuan', '奇幻': 'qihuan', '仙侠': 'xianxia', '都市': 'dushi',
'游戏': 'youxi', '科幻': 'kehuan', '轻小说': '2cy', '武侠': 'wuxia',
'现实': 'xianshi', '军事': 'junshi', '历史': 'lishi', '体育': 'tiyu',
'诸天无限': 'zhutianwuxian', '悬疑': 'lingyi', '古代言情': 'gdyq',
'现代言情': 'xdyq', '玄幻言情': 'xhyq'
};
var GENRE_LIST = Object.keys(GENRE_MAP);
function _trim(s) {
if (!s) return '';
return String(s).replace(/^[\s\u3000\u00A0]+|[\s\u3000\u00A0]+$/g, '').trim();
}
function _isCfBlocked(html) {
if (!html) return true;
var l = html.toLowerCase();
return l.indexOf('cloudflare') >= 0 || l.indexOf('cf-') >= 0 ||
l.indexOf('checking your browser') >= 0 || l.indexOf('ray id') >= 0 ||
html.length < 1000;
}
async function _ensureCookies() {
legado.log('[initCookies] using preset CF_COOKIE');
}
async function _httpGet(url, referer) {
var headers = {
'User-Agent': UA,
'Cookie': CF_COOKIE,
'Referer': referer || BASE,
'Accept': DEFAULT_HEADERS['Accept'],
'Accept-Language': DEFAULT_HEADERS['Accept-Language'],
'Cache-Control': DEFAULT_HEADERS['Cache-Control'],
'Pragma': DEFAULT_HEADERS['Pragma'],
'Sec-Ch-Ua': DEFAULT_HEADERS['Sec-Ch-Ua'],
'Sec-Ch-Ua-Mobile': DEFAULT_HEADERS['Sec-Ch-Ua-Mobile'],
'Sec-Ch-Ua-Platform': DEFAULT_HEADERS['Sec-Ch-Ua-Platform'],
'Sec-Fetch-Dest': DEFAULT_HEADERS['Sec-Fetch-Dest'],
'Sec-Fetch-Mode': DEFAULT_HEADERS['Sec-Fetch-Mode'],
'Sec-Fetch-Site': referer ? 'same-origin' : 'none',
'Sec-Fetch-User': DEFAULT_HEADERS['Sec-Fetch-User'],
'Upgrade-Insecure-Requests': DEFAULT_HEADERS['Upgrade-Insecure-Requests']
};
return await legado.http.get(url, headers);
}
async function _httpPost(url, body, referer) {
var headers = {
'User-Agent': UA,
'Cookie': CF_COOKIE,
'Referer': referer || BASE,
'Accept': DEFAULT_HEADERS['Accept'],
'Accept-Language': DEFAULT_HEADERS['Accept-Language'],
'Cache-Control': DEFAULT_HEADERS['Cache-Control'],
'Pragma': DEFAULT_HEADERS['Pragma'],
'Sec-Ch-Ua': DEFAULT_HEADERS['Sec-Ch-Ua'],
'Sec-Ch-Ua-Mobile': DEFAULT_HEADERS['Sec-Ch-Ua-Mobile'],
'Sec-Ch-Ua-Platform': DEFAULT_HEADERS['Sec-Ch-Ua-Platform'],
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': referer ? 'same-origin' : 'none',
'Sec-Fetch-User': DEFAULT_HEADERS['Sec-Fetch-User'],
'Upgrade-Insecure-Requests': '1',
'Content-Type': 'application/x-www-form-urlencoded',
'Origin': BASE
};
return await legado.http.post(url, body, headers);
}
async function search(keyword, page) {
legado.log('[search] keyword=' + keyword + ' page=' + page);
var result = [];
await _ensureCookies();
var url = BASE + '/';
var postData = 's=' + encodeURIComponent(keyword);
var html = await _httpPost(url, postData, BASE + '/');
if (!html || html.length < 500 || _isCfBlocked(html)) {
var sessionId = null;
try {
sessionId = await legado.browser.acquire();
await legado.browser.navigate(sessionId, url, { waitFor: 'load' });
html = await legado.browser.html(sessionId);
} catch (e) {
legado.log('[search] browser error: ' + e.message);
} finally {
if (sessionId) {
try { await legado.browser.close(sessionId); } catch (e) {}
}
}
}
if (!html || html.length < 500 || _isCfBlocked(html)) {
return result;
}
var doc = legado.dom.parse(html);
var listItems = legado.dom.selectAll(doc, 'ul.my-5 li');
if (!listItems || listItems.length === 0) {
listItems = legado.dom.selectAll(doc, 'li');
}
for (var i = 0; i < listItems.length; i++) {
var item = listItems[i];
var linkEl = legado.dom.select(item, 'a[href*="/book/"]');
if (!linkEl) continue;
var bookUrl = legado.dom.attr(linkEl, 'href');
var name = legado.dom.text(linkEl);
var itemHtml = legado.dom.html(item);
var author = '';
var authorMatch = itemHtml.match(/作者:([^/<\s]+)/);
if (authorMatch) {
author = authorMatch[1].trim();
}
if (bookUrl && name) {
if (bookUrl.indexOf('http') !== 0) {
bookUrl = BASE + bookUrl;
}
result.push({
name: _trim(name),
author: _trim(author),
bookUrl: bookUrl,
coverUrl: '',
intro: '',
kind: '',
lastChapter: ''
});
}
}
legado.log('[search] found=' + result.length);
legado.dom.free(doc);
return result;
}
async function bookInfo(bookUrl) {
legado.log('[bookInfo] url=' + bookUrl);
var result = {
name: '',
author: '',
bookUrl: bookUrl,
coverUrl: '',
intro: '',
kind: '',
lastChapter: '',
tocUrl: bookUrl
};
var html = await _httpGet(bookUrl);
if (!html || html.length < 500) {
var sessionId = null;
try {
sessionId = await legado.browser.acquire();
await legado.browser.navigate(sessionId, bookUrl);
html = await legado.browser.html(sessionId);
} catch (e) {
legado.log('[bookInfo] browser error: ' + e.message);
} finally {
if (sessionId) {
try { await legado.browser.close(sessionId); } catch (e) {}
}
}
}
if (!html || html.length < 500) {
return result;
}
var doc = legado.dom.parse(html);
var nameEl = legado.dom.select(doc, 'h1');
if (nameEl) {
result.name = _trim(legado.dom.text(nameEl));
}
if (!result.name) {
var nm = html.match(/<h1[^>]*>([^<]+)<\/h1>/);
if (nm && nm[1]) {
result.name = _trim(nm[1]);
}
}
var authorEl = legado.dom.select(doc, 'a[href*="s="]');
if (authorEl) {
result.author = _trim(legado.dom.text(authorEl));
}
if (!result.author) {
var allLinks = legado.dom.selectAll(doc, 'a');
for (var i = 0; i < allLinks.length; i++) {
var txt = legado.dom.text(allLinks[i]);
if (txt && txt.length > 1 && txt.length < 20 && txt.indexOf('作者') < 0) {
var par = legado.dom.select(allLinks[i], '..');
if (par) {
var parTxt = legado.dom.text(par);
if (parTxt && parTxt.indexOf('作者') >= 0) {
result.author = txt;
break;
}
}
}
}
}
var introEl = legado.dom.select(doc, 'span.long-text');
if (!introEl) {
introEl = legado.dom.select(doc, 'p.collapsible-text');
}
if (!introEl) {
introEl = legado.dom.select(doc, '.book-intro');
}
if (introEl) {
result.intro = _trim(legado.dom.text(introEl));
}
var lastChEl = legado.dom.select(doc, 'a.chapter-link');
if (lastChEl) {
result.lastChapter = _trim(legado.dom.text(lastChEl));
}
legado.log('[bookInfo] name=' + result.name + ' author=' + result.author);
legado.dom.free(doc);
return result;
}
async function chapterList(tocUrl) {
legado.log('[chapterList] url=' + tocUrl);
var result = [];
var html = await _httpGet(tocUrl);
if (!html || html.length < 500) {
var sessionId = null;
try {
sessionId = await legado.browser.acquire();
await legado.browser.navigate(sessionId, tocUrl);
html = await legado.browser.html(sessionId);
} catch (e) {
legado.log('[chapterList] browser error: ' + e.message);
} finally {
if (sessionId) {
try { await legado.browser.close(sessionId); } catch (e) {}
}
}
}
if (!html || html.length < 500) {
return result;
}
var doc = legado.dom.parse(html);
var links = legado.dom.selectAll(doc, 'a.chapter-link');
if (!links || links.length === 0) {
links = legado.dom.selectAll(doc, 'a[href*="/chapter/"]');
}
var seen = {};
for (var i = 0; i < links.length; i++) {
var link = links[i];
var url = legado.dom.attr(link, 'href');
var name = legado.dom.text(link);
if (url && name && url.indexOf('/chapter/') !== -1) {
if (url.indexOf('http') !== 0) {
url = BASE + url;
}
if (!seen[url]) {
seen[url] = true;
result.push({
name: _trim(name),
url: url
});
}
}
}
legado.log('[chapterList] found=' + result.length);
legado.dom.free(doc);
return result;
}
async function chapterContent(chapterUrl) {
legado.log('[chapterContent] url=' + chapterUrl);
var content = '';
var html = await _httpGet(chapterUrl);
if (!html || html.length < 200) {
var sessionId = null;
try {
sessionId = await legado.browser.acquire();
await legado.browser.navigate(sessionId, chapterUrl);
html = await legado.browser.html(sessionId);
} catch (e) {
legado.log('[chapterContent] browser error: ' + e.message);
} finally {
if (sessionId) {
try { await legado.browser.close(sessionId); } catch (e) {}
}
}
}
if (!html || html.length < 200) {
return content;
}
var doc = legado.dom.parse(html);
var contentEl = legado.dom.select(doc, '#chapter-content');
if (!contentEl) {
contentEl = legado.dom.select(doc, '.chapter-content');
}
if (!contentEl) {
contentEl = legado.dom.select(doc, 'article');
}
if (contentEl) {
legado.dom.remove(contentEl, 'script, style, nav, footer, header');
content = legado.dom.html(contentEl);
}
if (!content || content.length < 100) {
var bodyEl = legado.dom.select(doc, 'body');
if (bodyEl) {
legado.dom.remove(bodyEl, 'script, style, nav, footer, header, .nav, .footer, .header');
content = legado.dom.html(bodyEl);
}
}
if (content) {
content = content.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '');
content = content.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '');
content = content.replace(/<nav[^>]*>[\s\S]*?<\/nav>/gi, '');
content = content.replace(/<footer[^>]*>[\s\S]*?<\/footer>/gi, '');
content = content.replace(/<header[^>]*>[\s\S]*?<\/header>/gi, '');
content = content.replace(/<p[^>]*>([\s\S]*?)<\/p>/gi, '\n$1\n');
content = content.replace(/<br\s*\/?>/gi, '\n');
content = content.replace(/<[^>]+>/g, '');
content = content.replace(/ /g, ' ');
content = content.replace(/&/g, '&');
content = content.replace(/</g, '<');
content = content.replace(/>/g, '>');
content = content.replace(/"/g, '"');
content = content.replace(/'/g, "'");
content = content.replace(/—/g, '—');
content = content.replace(/–/g, '–');
content = content.replace(/\n{3,}/g, '\n\n');
content = _trim(content);
}
legado.log('[chapterContent] length=' + (content ? content.length : 0));
legado.dom.free(doc);
return content;
}
async function explore(page, category) {
legado.log('[explore] page=' + page + ' category=' + category);
var result = [];
if (!category || category === 'GETALL') {
return GENRE_LIST.slice();
}
var slug = GENRE_MAP[category];
if (!slug) {
slug = category;
}
var p = page && page > 1 ? page : 1;
var url = BASE + '/genre/' + slug + '/page/' + p + '/';
var html = await _httpGet(url);
if (!html || html.length < 500) {
var sessionId = null;
try {
sessionId = await legado.browser.acquire();
await legado.browser.navigate(sessionId, url);
html = await legado.browser.html(sessionId);
} catch (e) {
legado.log('[explore] browser error: ' + e.message);
} finally {
if (sessionId) {
try { await legado.browser.close(sessionId); } catch (e) {}
}
}
}
if (!html || html.length < 500) {
return result;
}
var doc = legado.dom.parse(html);
var cards = legado.dom.selectAll(doc, 'div.col-12.col-md-4.mb-4');
if (!cards || cards.length === 0) {
cards = legado.dom.selectAll(doc, '[class*="mb-4"]');
}
for (var i = 0; i < cards.length; i++) {
var card = cards[i];
var linkEl = legado.dom.select(card, 'h5 a, h4 a');
if (!linkEl) {
linkEl = legado.dom.select(card, 'a[href*="/book/"]');
}
if (!linkEl) continue;
var bookUrl = legado.dom.attr(linkEl, 'href');
var name = legado.dom.text(linkEl);
if (bookUrl && name) {
if (bookUrl.indexOf('http') !== 0) {
bookUrl = BASE + bookUrl;
}
result.push({
name: _trim(name),
author: '',
bookUrl: bookUrl,
coverUrl: '',
kind: category,
lastChapter: ''
});
}
}
legado.log('[explore] found=' + result.length);
legado.dom.free(doc);
return result;
}
async function TEST(type) {
if (type === '__list__') {
return ['search', 'explore', 'bookInfo', 'chapterList', 'chapterContent'];
}
if (type === 'search') {
var results = await search('斗破苍穹', 1);
return { passed: results.length > 0, message: 'search found=' + results.length + (results.length > 0 ? ' first=' + results[0].name : '') };
}
if (type === 'explore') {
var cats = await explore(1, 'GETALL');
var books = await explore(1, '玄幻');
return { passed: cats.length >= 10 && books.length > 0, message: 'explore cats=' + cats.length + ' books=' + books.length };
}
if (type === 'bookInfo') {
var r = await bookInfo('https://moyun.io/book/2d51270c-f172-4c43-8f82-f0b59245ca4d');
return { passed: !!r.name, message: 'bookInfo name=' + r.name + ' author=' + r.author };
}
if (type === 'chapterList') {
var r = await chapterList('https://moyun.io/book/2d51270c-f172-4c43-8f82-f0b59245ca4d');
return { passed: r.length > 0, message: 'chapterList cnt=' + r.length + ' first=' + (r[0] ? r[0].name : 'N/A') };
}
if (type === 'chapterContent') {
var r = await chapterContent('https://moyun.io/chapter/d91edc22188544ecbc06ddab73fb0ccf.html');
return { passed: r.length > 100, message: 'chapterContent len=' + r.length };
}
return { passed: false, message: 'Unknown test type: ' + type };
}