墨云阁

https://moyun.io

zpccool (13551) 16小时前 下载:314

小说
墨云阁小说网 - 免费无弹窗小说阅读
二维码导入(APP尚未完成该功能)
// @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(/&nbsp;/g, ' ');
        content = content.replace(/&amp;/g, '&');
        content = content.replace(/&lt;/g, '<');
        content = content.replace(/&gt;/g, '>');
        content = content.replace(/&quot;/g, '"');
        content = content.replace(/&#39;/g, "'");
        content = content.replace(/&mdash;/g, '—');
        content = content.replace(/&ndash;/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 };
}
广告