Greasy Fork

Greasy Fork is available in English.

轻小说文库+

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

当前为 2021-04-01 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         轻小说文库+
// @namespace    Wenku8+
// @version      0.8.5
// @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';

        // Prevent URL.revokeObjectURL in script 轻小说文库下载
        const Ori_revokeObjectURL = URL.revokeObjectURL;
        URL.revokeObjectURL = function(arg) {
            if (typeof(arg) === 'string' && arg.substr(0, 5) === 'blob:') {return false;};
            return Ori_revokeObjectURL(arg);
        }

        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;
                if (allImages[i].src.substr(0,5) === 'blob:') {
                    const image = new Image();
                    image.onload = function() {
                        saveBlobToFile(toImageFormatURL(image, 1), imageName);
                        dlIncrease(button);
                    }
                    image.src = url;
                } else {
                    download(url, imageName, button);
                }
            }
        }

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

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

                    // toImageFormatURL
                    const image = new Image();
                    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);
                        dlIncrease(displayElement);
                    };
                }
            })

            return true;
        }

        // Increase dlCompleted and judge dlAllRunning
        function dlIncrease(displayElement) {
            // 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';
                }
            }
        }

        // 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);
    }
})();