笔趣阁下载器

可在笔趣阁(beqege.cc)下载小说,在小说目录页面使用。(仅供交流,可能存在bug)。

目前為 2024-07-14 提交的版本,檢視 最新版本

// ==UserScript==
// @name         笔趣阁下载器
// @namespace    http://tampermonkey.net/
// @version      0.2.3
// @description  可在笔趣阁(beqege.cc)下载小说,在小说目录页面使用。(仅供交流,可能存在bug)。
// @author       Yearly
// @match        https://www.beqege.cc/*/
// @license      MIT
// @grant        GM_registerMenuCommand
// @grant        GM_addStyle
// @namespace    https://gf.qytechs.cn/scripts/500170
// @supportURL   https://gf.qytechs.cn/scripts/500170
// @homepageURL  https://gf.qytechs.cn/scripts/500170
// @icon         https://www.beqege.cc/favicon.ico
// ==/UserScript==

(function() {

    // 添加自定义样式
    GM_addStyle(`
        #fetchContentModal {
            border-radius: 10px;
            position: fixed;
            top: 40%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: white;
            padding: 5px 20px 10px 20px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
            z-index: 10000;
            width: 350px;
            text-align: center;
        }
        #fetchContentModal input[type="number"] {
            width: 30%;
            margin: 5px 0;
            text-align: center;
        }
        #fetchContentModal button {
            width: 100%;
            margin: 10px 0;
        }
        #fetchContentProgress {
            width: 100%;
            background: #f3f3f3;
            border: 1px solid #ccc;
            margin: 10px 0;
        }
        #fetchContentProgress div {
            width: 0;
            height: 20px;
            background: #4caf50;
            text-align: center;
            margin-left: 0;
            color: #960;
            white-space: nowrap;
        }
    `);

    // 创建悬浮窗
    const modalHtml = `
        <div id="fetchContentModal" style="display:none;">
            <h3>小说下载工具<span id="fetcModalClose" style="cursor: pointer; float: right; margin:-8px -4px;">✕</span></h3>
            <label for="ranges">下载章节范围:</label>
            <input type="number" id="_startRange" min="1" value="1">~
            <input type="number" id="_endRange" min="1" value="2">
            <label id="_warn_info"></label>
            <button id="fetchContentButton">开始下载</button>
            <div id="fetchContentProgress">
                <div></div>
            </div>
            <a id="_downlink"></a>
        </div>
    `;
    document.body.insertAdjacentHTML('beforeend', modalHtml);

    // 获取元素
    const modal = document.getElementById('fetchContentModal');
    const startRangeInput = document.getElementById('_startRange');
    const endRangeInput = document.getElementById('_endRange');
    const fetchButton = document.getElementById('fetchContentButton');
    const progressBar = document.getElementById('fetchContentProgress').firstElementChild;
    const downlink = document.getElementById('_downlink');
    const warnInfo = document.getElementById('_warn_info');
    const fetcClose = document.getElementById('fetcModalClose');

    // 注册(不可用)菜单命令
    GM_registerMenuCommand('小说下载工具', () => {
        modal.style.display = 'block';
        startRangeInput.max = document.querySelectorAll("#list > dl > dd > a").length;
        endRangeInput.max = document.querySelectorAll("#list > dl > dd > a").length;
        endRangeInput.value = endRangeInput.max;
        warnInfo.innerText=`当前小说共 ${endRangeInput.max} 章。\n设置范围后点击开始下载,并稍作等待。\n若章节过多下载卡住,可尝试减小章节范围分次下载。`
    });

    fetcClose.addEventListener('click', async () => {
        modal.style.display = 'none';
    });

    // 下载
    fetchButton.addEventListener('click', async () => {
        downlink.innerText = "";
        downlink.href = null;
        downlink.download = null;

        const startRange = parseInt(startRangeInput.value, 10);
        const endRange = parseInt(endRangeInput.value, 10);

        const links = document.querySelectorAll("#list > dl > dd > a");

        const selectedLinks = Array.from(links).slice(startRange - 1, endRange);

        const title = document.querySelector("#maininfo #info h1").innerText || document.title;

        const results = [];
        const totalLinks = selectedLinks.length;
        let completedRequests = 0;

        const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

        const fetchAndParse = async (link, index) => {
            await delay(index * 5);
            const url = link.href;
            try {
                const response = await fetch(url);
                if (!response.ok) {
                    throw new Error(`HTTP error! Status: ${response.status}`);
                }
                const text = await response.text();
                const parser = new DOMParser();
                const doc = parser.parseFromString(text, 'text/html');
                const contentElements = doc.querySelectorAll('div#content > :not(div#device)');

                let content = '';
                contentElements.forEach((element) => {
                    content += element.innerText + '\n';
                });

                results[index] = { title: link.innerText, content: content.trim() };
            } catch (error) {
                results[index] = { title: link.innerText, content: `Error fetching ${url}: ${error}` };
            } finally {
                // 更新进度条
                completedRequests++;
                const progress = Math.round((completedRequests / totalLinks) * 100);
                progressBar.style.width = `${progress}%`;
                progressBar.textContent = `${completedRequests} / ${totalLinks}`;
            }
        };

        Promise.all(selectedLinks.map((link, index) => fetchAndParse(link, index)))
            .then(() => {
            let finalResults = document.querySelector("#maininfo #info").innerText || title || '';
            finalResults += `\n\n下载章节范围:${startRange} ~ ${endRange}\n`;
            finalResults += "\n-----------------------\n";

            results.forEach((result) => {
                finalResults += `\n## ${result.title}\n`;
                finalResults += result.content + '\n';
            });

            finalResults += "\n-----------------------\n";

            const blob = new Blob([finalResults], { type: 'text/plain' });
            downlink.innerText = "若未开始自动下载,点击这里";
            downlink.href = URL.createObjectURL(blob);
            downlink.download = `${title}_${startRange}~${endRange}.txt`;
            downlink.click();
        })
            .catch((error) => {
            console.error('Error fetching links:', error);
        });

    });
})();

QingJ © 2025

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