轻小说文库+

章节批量下载,版权限制小说TXT简繁全本下载,书名/作者名双击复制,Ctrl+Enter快捷键发表书评,单章节下载,小说JPEG插图下载,下载线路点击切换

目前為 2021-03-31 提交的版本,檢視 最新版本

// ==UserScript==
// @name         轻小说文库+
// @namespace    Wenku8+
// @version      0.8.4
// @description  章节批量下载,版权限制小说TXT简繁全本下载,书名/作者名双击复制,Ctrl+Enter快捷键发表书评,单章节下载,小说JPEG插图下载,下载线路点击切换
// @author       PY-DNG
// @match        http*://www.wenku8.net/*
// @connect      dl.wenku8.com
// @connect      dl2.wenku8.com
// @connect      dl3.wenku8.com
// @connect      picture.wenku8.com
// @grant        GM_download
// @grant        GM_xmlhttpRequest
// ==/UserScript==

(function() {
    'use strict';

    // CONSTS
    const HTML_DOWNLOAD_CONTENER = '<div id="dctn" style=\"margin:0px auto;overflow:hidden;\">\n<fieldset style=\"width:820px;height:35px;margin:0px auto;padding:0px;\">\n<legend><b>《{BOOKNAME}》小说TXT简繁全本下载</b></legend>\n</fieldset>\n</div>';
    const HTML_DOWNLOAD_LINKS = '<div class="even">\n<span>简体(G)(<a class="dlink" href="http://dl.wenku8.com/down.php?type=txt&amp;id={BOOKID}" target="_black">载点一</a> \n<a class="dlink" href="http://dl.wenku8.com/down.php?type=txt&amp;id={BOOKID}&amp;fname=%B0%B2%B4%EF%D3%EB%B5%BA%B4%E5" target="_black">载点二</a>)</span>\n\n<span>简体(U)(<a class="dlink" href="http://dl.wenku8.com/down.php?type=utf8&amp;id={BOOKID}" target="_black">载点一</a> \n<a class="dlink" href="http://dl.wenku8.com/down.php?type=utf8&amp;id={BOOKID}&amp;fname=%B0%B2%B4%EF%D3%EB%B5%BA%B4%E5" target="_black">载点二</a>)</span>\n\n<span>繁体(U)(<a class="dlink" href="http://dl.wenku8.com/down.php?type=big5&amp;id={BOOKID}" target="_black">载点一</a> \n<a class="dlink" href="http://dl.wenku8.com/down.php?type=big5&amp;id={BOOKID}&amp;fname=%B0%B2%B4%EF%D3%EB%B5%BA%B4%E5" target="_black">载点二</a>)</span>\n  </div>';
    const HTML_DOWNLOAD_BOARD = '[轻小说文库+] 为您提供《{BOOKNAME}》的TXT简繁全本下载!</br>由此产生的一切法律及其他问题均由脚本用户承担</br>—— PY-DNG';
    const CSS_DOWNLOAD = '.even {display: grid; grid-template-columns: repeat(3, 1fr); text-align: center;} .dlink {text-align: center;}';
    const CSS_DOWNLOADPAGE = '.server {color: rgb(0, 160, 0);} .server:hover {color: rgb(0, 100, 0);} .server:focus {color: rgb(90, 180, 90);}';

    const TEXT_TIP_COPY = '双击复制';
    const TEXT_TIP_SERVERCHANGE = '点击切换线路';
    const TEXT_GUI_DOWNLOAD_IMAGE = '下载图片';
    const TEXT_GUI_DOWNLOAD_TEXT = '下载本章';
    const TEXT_GUI_DOWNLOADING = ' 下载中...'; const REG_GUI_DOWNLOADING = new RegExp(TEXT_GUI_DOWNLOADING + '$');
    const TEXT_GUI_DOWNLOADED = ' (下载完毕)'; const REG_GUI_DOWNLOADED = new RegExp(TEXT_GUI_DOWNLOADED.replaceAll(/([\(\)])+/g, '\\$1') + '$');
    const TEXT_GUI_DOWNLOADING_ALL = '下载中...(C/A)';
    const TEXT_GUI_DOWNLOADED_ALL = '下载图片(已完成)';

    // Get tab url api part
    const API = window.location.href.replace(/https?:\/\/www\.wenku8\.net\//, '').replace(/\?.*/, '')
                .replace(/^book\/\d+\.html?/, 'book').replace(/novel\/(\d+\/?)+\.html?$/, 'novel');
    switch (API) {
        // Dwonload page
        case 'modules/article/packshow.php':
            pageDownload();
            break;
        case 'modules/article/reviews.php':
        case 'modules/article/reviewshow.php':
            pageReview();
            break;
        // Index page
        case 'index.php':
            pageIndex();
            break;
        // Book page
        case 'book':
            pageBook();
            break;
        // Novel page
        case 'novel':
            pageNovel();
            break;
        // Other pages
        default:
            console.log(API);
    }

    // Book page add-on
    function pageBook() {
        const bookIdText = location.href.match(/\/(\d+)\.htm/)[1];
        const bookNameElement = document.querySelector('#content > div:nth-child(1) > table:nth-child(1) > tbody:nth-child(1) > tr:nth-child(1) > td:nth-child(1) > table:nth-child(1) > tbody:nth-child(1) > tr:nth-child(1) > td:nth-child(1) > span:nth-child(1) > b:nth-child(1)');
        const bookName = bookNameElement.innerText;
        const authorNameElement = document.querySelector('#content > div:nth-child(1) > table:nth-child(1) > tbody:nth-child(1) > tr:nth-child(2) > td:nth-child(2)');
        const authorName = authorNameElement.innerText.substr(authorNameElement.innerText.indexOf(':') + 1);
        const downloadEnabled = document.querySelector('#content > div:nth-child(1) > div > fieldset:nth-child(1) > legend:nth-child(1) > b:nth-child(1)') !== null;
        const commentArea = document.querySelector('#pcontent');
        const commentForm = document.querySelector('form[action^="https://www.wenku8.net/modules/article/reviews.php"]');
        const commentSbmt = document.querySelector('td > input[name="Submit"]');

        // Ctrl+Enter comment submit
        commentSbmt.value = '发表书评(Ctrl+Enter)';
        commentSbmt.style.padding = '0.3em 0.4em 0.3em 0.4em';
        commentSbmt.style.height= 'auto';
        commentArea.addEventListener('keydown', function() {
            let keycode = event.keyCode;
            if (keycode === 13 && event.ctrlKey && !event.altKey) {
                commentForm.submit();
            }
        })

        // Provide book & author name doubleclick copy
        bookNameElement.title = TEXT_TIP_COPY;
        authorNameElement.title = TEXT_TIP_COPY;
        bookNameElement.addEventListener('dblclick', function() {copyText(bookName);});
        authorNameElement.addEventListener('dblclick', function() {copyText(authorName);});

        // Provide txtfull download for book which download is disabled
        if (!downloadEnabled) {
            // Append download html model
            const modelContainer = document.createElement('div');
            document.querySelector('#content div').appendChild(modelContainer);
            modelContainer.outerHTML = HTML_DOWNLOAD_CONTENER.replaceAll('{BOOKNAME}', bookName);
            //document.querySelector('#content div').innerHTML += HTML_DOWNLOAD_CONTENER.replaceAll('{BOOKNAME}', bookName);
            document.querySelector('#content div').lastChild.querySelector('fieldset').innerHTML += HTML_DOWNLOAD_LINKS.replaceAll('{BOOKID}', bookIdText);
            // Append CSS
            addStyle(CSS_DOWNLOAD);
            // Write textboard
            let textBoard = document.querySelector('#content > div:nth-child(1) > table:nth-child(4) > tbody:nth-child(1) > tr:nth-child(1) > td:nth-child(2) > span:nth-child(1) > b:nth-child(2)');
            textBoard.innerHTML = HTML_DOWNLOAD_BOARD.replaceAll('{BOOKNAME}', bookName);
            textBoard.style.color = 'green';
        }
    }

    // Review page add-on
    function pageReview() {
        const commentArea = document.querySelector('#pcontent');
        const commentForm = document.querySelector('form[action^="https://www.wenku8.net/modules/article/review"]');
        const commentSbmt = document.querySelector('td > input[name="Submit"]');

        // Ctrl+Enter comment submit
        commentSbmt.value = '发表书评(Ctrl+Enter)';
        commentSbmt.style.padding = '0.3em 0.4em 0.3em 0.4em';
        commentSbmt.style.height= 'auto';
        commentArea.addEventListener('keydown', function() {
            let keycode = event.keyCode;
            if (keycode === 13 && event.ctrlKey && !event.altKey) {
                commentForm.submit();
            }
        })
    }

    // Novel page add-on
    function pageNovel() {
        const title = document.querySelector('#title').textContent;
        const isImagePage = title.includes('插图') || title.includes('插圖');
        const rightButtonDiv = document.querySelector('#linkright');
        const rightButtons = rightButtonDiv.childNodes;

        let dlCompleted = 0; // number of completed download tasks
        let dlAllCount = 0; // number of all download tasks
        let dlAllRunning = false; // whether there is downloadAllImages running

        // append control buttons
        let i;
        let spliter, button = rightButtonDiv.querySelector('a').cloneNode();
        for (i = 0; i < rightButtons.length; i++) {
            if (rightButtons[i].textContent.includes('|')) {
                spliter = rightButtons[i].cloneNode();
            }
        }

        // Attributes & Display config
        let allImages, buttonText;
        let clickFunc;
        if (isImagePage) {
            // get all images
            allImages = document.querySelectorAll('#content > div.divimage img');
            buttonText = TEXT_GUI_DOWNLOAD_IMAGE;
            clickFunc = function() {downloadAllImages();};
        } else {
            buttonText = TEXT_GUI_DOWNLOAD_TEXT;
            clickFunc = function() {downloadText();};
        }

        button.href = 'javascript:void(0);';
        button.target = '';
        button.innerText = buttonText;
        button.style.color = '#00BB00';
        button.addEventListener('click', clickFunc);
        rightButtonDiv.insertBefore(spliter, rightButtonDiv.lastChild);
        rightButtonDiv.insertBefore(button, rightButtonDiv.lastChild);
        rightButtonDiv.style.width = '500px';

        function downloadText() {
            const contentEle = document.querySelector('#content');
            let content = contentEle.innerText//.replaceAll('\n', '\r\n');

            if (content.length === 0) {
                return false;
            }

            // Clear spaces
            content = content.split('\n');
            for (let i = 0; i < content.length; i++) {
                content[i] = content[i].trim();
            }
            content = content.join('\r\n');

            // Download
            const blob = new Blob([content],{type:"text/plain;charset=utf-8"});
            const url = URL.createObjectURL(blob);
            const name = title + '.txt';

            const a = document.createElement('a');
            a.style.display = 'none';
            a.href = url;
            a.download = name;
            a.click();
        }

        function downloadAllImages() {
            if (dlAllRunning) {
                return false;
            }
            dlAllCount = allImages.length;
            dlCompleted = 0;
            dlAllRunning = true;
            // Display
            button.innerText = TEXT_GUI_DOWNLOADING_ALL.replace('C', '0').replace('A', String(dlAllCount));
            rightButtonDiv.style.width = '550px';
            // Download
            for (let i = 0; i < dlAllCount; i++) {
                const imageName = title + '_' + String(i+1) + '.jpg';
                const url = allImages[i].src.substr(0,5) === 'blob:' ? toImageFormatURL(allImages[i], 1) : allImages[i].src;
                download(url, imageName, button);
            }
        }

        // File download function
        function download(url, name, displayElement) {
            // Check
            if (!url || !name) {
                return false;
            }

            // if .src is already changed loaded with blob by another script, use it directly
            if (url.substr(0,5) === 'blob:') {
                toImageFormatURL(image, 1);
                saveBlobToFile(url, name);
                return true;
            }

            // xmlHTTPRequest
            GM_xmlhttpRequest({
                method:       'GET',
                url:          url,
                responseType: 'blob',
                onload:       function(request) {
                    // DataURL
                    let objURL = URL.createObjectURL(request.response);

                    // toImageFormatURL
                    const image = document.createElement('img');
                    image.src = objURL;
                    image.onload = function() {
                        //image.style.display = 'none';
                        //document.body.appendChild(image);
                        const formatURL = toImageFormatURL(image, 1);
                        //document.body.removeChild(image);

                        saveBlobToFile(formatURL, name);

                        // Task count decrease
                        dlCompleted++;
                        if (dlCompleted === dlAllCount) {
                            dlAllRunning = false;
                        }

                        // Display
                        if (displayElement) {
                            displayElement.innerText = TEXT_GUI_DOWNLOADING_ALL
                                .replace('C', String(dlCompleted)).replace('A', String(dlAllCount));
                            if (!dlAllRunning) {
                                displayElement.innerText = TEXT_GUI_DOWNLOADED_ALL;
                                rightButtonDiv.style.width = '550px';
                            }
                        }
                    };
                }
            })

            return true;
        }

        // Blob url file saving function
        function saveBlobToFile(blobURL, name) {
            // Create <a>
            const a = document.createElement('a');
            a.style.display = 'none';
            a.href = blobURL;
            a.download = name;
            a.click();
        }

        // Image format changing function
        function toImageFormatURL(image, format) {
            if (typeof(format) === 'number') {format = ['image/jpeg', 'image/png', 'image/webp'][format-1]}
            const cvs = document.createElement('canvas');
            cvs.width = image.width;
		    cvs.height = image.height;
            const ctx = cvs.getContext('2d');
            ctx.drawImage(image, 0, 0);
            return cvs.toDataURL(format);
        }
    }

    // Index page add-on
    function pageIndex() {
    }

    // Download page add-on
    function pageDownload() {
        let i;
        let dlCount = 0; // number of active download tasks
        let dlAllRunning = false; // whether there is downloadAll running
        /* ******************* GUI ******************* */
        // Create left operation GUI
        let downloadGUI = document.querySelectorAll('#left div.block')[1].cloneNode(true);
        // Rename title
        downloadGUI.querySelector('.blocktitle .txt').innerHTML = '下载全部章节';
        // Remove content
        downloadGUI.removeChild(downloadGUI.querySelector('.blockcontent'));
        // Create operation ul list
        let optionButtonsForm = document.querySelector('#left div.block div.blockcontent div ul[style]').cloneNode(true);
        // Reset lis
        const NAMES = ['本地简体(G)', '本地简体(U)', '本地繁体(U)', '地址二简体(G)', '地址二简体(U)', '地址二繁体(U)'];
        let lis = optionButtonsForm.querySelectorAll('li');
        let li = lis[0].cloneNode(true);
        let newli;
        li.querySelector('a').href = 'javascript:void(0);';
        li.querySelector('a').className = '';
        li.querySelector('a').classList.add('server');
        li.querySelector('a').innerHTML = '默认按钮文本';
        for (i = 0; i < 6; i++) {
            // If li exist, remove it
            if (lis[i]) {
                optionButtonsForm.removeChild(lis[i]);
            };
            // Create a new one
            newli = li.cloneNode(true);
            // Modify name
            newli.querySelector('a').innerHTML = NAMES[i];
            // Mark i
            newli.i = i;
            // Append it
            optionButtonsForm.appendChild(newli);
            // Add event listener
            newli.addEventListener('click',
            function() { // i refers to its current value in loop by marking on the li element
                downloadAll(this.i);
            })
        }
        // Create a container
        let blockcontent = document.createElement('div');
        blockcontent.classList.add('blockcontent');
        blockcontent.style.paddingLeft = '10px';
        // Append ul
        blockcontent.appendChild(optionButtonsForm);
        // Append container
        downloadGUI.appendChild(blockcontent);
        // Append GUI
        document.querySelector('#left').appendChild(downloadGUI);

        // Servers GUI
        let servers = document.querySelectorAll('#content>b');
        let serverEles = [];
        for (i = 0; i < servers.length; i++) {
            if (servers[i].innerText.includes('wenku8.com')) {
                serverEles.push(servers[i]);
            }
        }
        for (i = 0; i < serverEles.length; i++) {
            //serverEles[i].style.color = 'green';
            serverEles[i].classList.add('server');
            serverEles[i].title = TEXT_TIP_SERVERCHANGE;
            serverEles[i].addEventListener('click', function() {changeAllServers(this.innerText);});
        }
        addStyle(CSS_DOWNLOADPAGE);

        /* ******************* Code ******************* */
        // Change all server elements
        function changeAllServers(server) {
            let i;
            const allA = document.querySelectorAll('.even a');
            for (i = 0; i < allA.length; i++) {
                changeServer(server, allA[i]);
            }
        }

        // Change server for an element
        function changeServer(server, element) {
            if (!element.href) {return false;};
            element.href = element.href.replace(/\/\/dl\d?\.wenku8\.com\//g, '//' + server + '/');
        }

        // Get novel name
        const novelName = document.querySelector('html body div.main div#centerm div#content table.grid caption a').innerText;
        let downloadAll = function(type) {
            // Check: only download while no download active tasks currently
            if (dlAllRunning) {
                return false;
            }
            dlAllRunning = true;

            // GUI display
            downloadGUI.querySelector('.blocktitle .txt').innerHTML = TEXT_GUI_DOWNLOADING;

            // Name customize
            let NAME = novelName + ' {j}.';
            let allNames = getAllNames();
            if (window.location.href.indexOf('txt') != -1) {
                NAME += 'txt';
            } else {
                NAME += document.querySelector('html body div.main div#centerm div#content table.grid tbody tr td.even a').innerText.replace(/[^\w]+/, '').toLowerCase();
            }
            let i,j = 0;
            const allA = document.querySelectorAll('.even a');
            for (i = type; i < allA.length; i = i + 6) {
                /*GM_download({
                    url: allA[i].href,
                    name: NAME.replace('{j}', (window.location.href.indexOf('txtfull') === -1 ? allNames[j] : ''))
                });*/
                download(
                    allA[i].href,
                    NAME.replace('{j}', (window.location.href.indexOf('txtfull') === -1 ? allNames[j] : '')),
                    allA[i].parentElement.parentElement.querySelector('td.odd')
                )
                j += 1;
            }
            downloadGUI.querySelector('.blocktitle .txt').innerHTML = '下载全部章节';
        }

        function getAllNames() {
            let all = document.querySelectorAll('.grid tbody tr .odd');
            let names = [];
            for (let i = 0; i < all.length; i++) {
                names[i] = all[i].innerText.replace(REG_GUI_DOWNLOADED, '').replace(REG_GUI_DOWNLOADING,'');
            }
            return names;
        }

        // File download function
        function download(url, name, displayElement) {
            // Check
            if (!url || !name) {
                return false;
            }

            // dl task count increase
            dlCount++;

            // Display
            let text = '';
            if (displayElement) {
                if (displayElement.innerText) {text = displayElement.innerText.replace(REG_GUI_DOWNLOADED, '').replace(REG_GUI_DOWNLOADING,'');};
                displayElement.innerText = text + TEXT_GUI_DOWNLOADING;
            }

            // xmlHTTPRequest
            GM_xmlhttpRequest({
                method:       'GET',
                url:          url,
                responseType: 'blob',
                onload:       function(request) {
                    // DataURL
                    let objURL = URL.createObjectURL(request.response);

                    // Create <a>
                    const a = document.createElement('a');
                    a.style.display = 'none';
                    a.href = objURL;
                    a.download = name;
                    a.click();

                    // Task count decrease
                    dlCount--;
                    if (dlCount === 0) {
                        dlAllRunning = false;
                    }

                    // Display
                    if (displayElement) {
                        displayElement.innerText = TEXT_GUI_DOWNLOADED.replace(/^ /, '');
                        if (text) {displayElement.innerText = text + TEXT_GUI_DOWNLOADED;};
                    }
                }
            })

            return true;
        }
    }

    function addStyle(css) {
        document.head.appendChild(document.createElement("style")).textContent = css;
    }

    function copyText(text) {
        // Create a new textarea for copying
        const newInput = document.createElement('textarea');
        document.body.appendChild(newInput);
        newInput.value = text;
        newInput.select();
        document.execCommand('copy');
        document.body.removeChild(newInput);
    }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址