网页漫画下载为epub/mobi/pdf等电子书格式

将网页漫画下载下来方便导入墨水屏电子书进行阅读

目前為 2024-11-15 提交的版本,檢視 最新版本

// ==UserScript==
// @name         网页漫画下载为epub/mobi/pdf等电子书格式
// @namespace    http://tampermonkey.net/
// @version      1.1.0
// @description  将网页漫画下载下来方便导入墨水屏电子书进行阅读
// @author       MornLight
// @match        https://m.rumanhua.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=greasyfork.org
// @grant        GM_xmlhttpRequest
// @require      https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js
// @run-at       document-end
// @license      MIT
// @supportURL   https://duanmorningsir.github.io/
// ==/UserScript==

(function () {
    'use strict';

    // 创建下载按钮和输入框
    function createDownloadButton() {
        const container = document.createElement('div');
        container.style.position = 'fixed';
        container.style.bottom = '20px';
        container.style.right = '20px';
        container.style.zIndex = '1000';
        container.style.display = 'flex';
        container.style.flexDirection = 'column';
        container.style.gap = '12px';
        container.style.backgroundColor = 'rgba(255, 255, 255, 0.9)';
        container.style.padding = '15px';
        container.style.borderRadius = '5px';
        container.style.boxShadow = '0 0 10px rgba(0,0,0,0.1)';

        // 获取总页数
        const totalPages = document.querySelectorAll('div.chapter-img-box').length;

        // 起始页输入框
        const startInput = document.createElement('input');
        startInput.type = 'number';
        startInput.min = '1';
        startInput.max = totalPages;
        startInput.value = '1';
        startInput.placeholder = `起始页(1-${totalPages})`;
        startInput.style.width = '120px';
        startInput.style.padding = '8px';
        startInput.style.border = '1px solid #ccc';
        startInput.style.borderRadius = '5px';
        startInput.style.textAlign = 'center';
        startInput.style.fontSize = '14px';

        // 结束页输入框
        const endInput = document.createElement('input');
        endInput.type = 'number';
        endInput.min = '1';
        endInput.max = totalPages;
        endInput.value = totalPages; // 默认设为最大页数
        endInput.placeholder = `结束页(1-${totalPages})`;
        endInput.style.width = '120px';
        endInput.style.padding = '8px';
        endInput.style.border = '1px solid #ccc';
        endInput.style.borderRadius = '5px';
        endInput.style.textAlign = 'center';
        endInput.style.fontSize = '14px';

        // 添加输入验证
        startInput.addEventListener('change', () => {
            const startVal = parseInt(startInput.value);
            const endVal = parseInt(endInput.value);

            if (startVal < 1) {
                alert('起始页不能小于1');
                startInput.value = '1';
            } else if (startVal > totalPages) {
                alert(`起始页不能大于总页数${totalPages}`);
                startInput.value = '1';
            } else if (startVal > endVal) {
                alert('起始页不能大于结束页');
                startInput.value = endVal;
            }
        });

        endInput.addEventListener('change', () => {
            const startVal = parseInt(startInput.value);
            const endVal = parseInt(endInput.value);

            if (endVal > totalPages) {
                alert(`结束页不能大于总页数${totalPages}`);
                endInput.value = totalPages;
            } else if (endVal < startVal) {
                alert('结束页不能小于起始页');
                endInput.value = startVal;
            }
        });

        // 下载按钮
        const downloadButton = document.createElement('button');
        downloadButton.textContent = '下载指定范围漫画';
        downloadButton.style.padding = '10px';
        downloadButton.style.backgroundColor = '#007bff';
        downloadButton.style.color = '#fff';
        downloadButton.style.border = 'none';
        downloadButton.style.borderRadius = '5px';
        downloadButton.style.cursor = 'pointer';

        // 全部下载按钮
        const downloadAllButton = document.createElement('button');
        downloadAllButton.textContent = '下载整个章节';
        downloadAllButton.style.padding = '10px';
        downloadAllButton.style.backgroundColor = '#28a745';
        downloadAllButton.style.color = '#fff';
        downloadAllButton.style.border = 'none';
        downloadAllButton.style.borderRadius = '5px';
        downloadAllButton.style.cursor = 'pointer';

        // 添加事件监听
        downloadButton.addEventListener('click', () => downloadComic(startInput.value, endInput.value));
        downloadAllButton.addEventListener('click', () => downloadComic());

        container.appendChild(startInput);
        container.appendChild(endInput);
        container.appendChild(downloadButton);
        container.appendChild(downloadAllButton);
        document.body.appendChild(container);
    }

    // 获取章节名称
    function getChapterName() {
        const chapterNameElement = document.querySelector('.chaphead-name h1');
        return chapterNameElement ? chapterNameElement.textContent.trim() : '未知章节';
    }

    // 下载漫画
    async function downloadComic(startPage, endPage) {
        const chapterName = getChapterName();
        const chapterImgBoxes = document.querySelectorAll('div.chapter-img-box');
        const totalPages = chapterImgBoxes.length;

        // 确定起始和结束页
        const start = startPage ? parseInt(startPage, 10) : 1;
        const end = endPage ? parseInt(endPage, 10) : totalPages;

        // 验证页数
        if (start > end || start < 1 || end > totalPages) {
            alert(`请输入有效的页数范围!(1-${totalPages})`);
            return;
        }

        // 创建 PDF 实例
        const pdf = new jspdf.jsPDF();

        // 并行下载图片
        const downloadPromises = [];
        chapterImgBoxes.forEach((box, index) => {
            // 只下载指定范围内的图片
            if (index + 1 >= start && index + 1 <= end) {
                const img = box.querySelector('img');
                if (img && img.dataset.src) {
                    const imgUrl = img.dataset.src;
                    downloadPromises.push(downloadImage(imgUrl, index - start + 1, pdf, end - start + 1));
                }
            }
        });

        // 等待所有图片下载完成
        await Promise.all(downloadPromises);

        // 保存 PDF 文件
        const fileName = end === chapterImgBoxes.length && start === 1
            ? `${chapterName}_全部.pdf`
            : `${chapterName}_${start}-${end}.pdf`;
        pdf.save(fileName);

        // 显示下载完成的提示
        alert(`漫画《${chapterName}》第${start}至${end}页下载完成!`);
    }

    // 下载图片并添加到 PDF
    function downloadImage(url, index, pdf, maxImages) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: url,
                responseType: 'blob',
                onload: function (response) {
                    try {
                        const blob = response.response;
                        const reader = new FileReader();
                        reader.onload = function (event) {
                            const imgData = event.target.result;
                            pdf.addImage(imgData, 'JPEG', 0, 0, pdf.internal.pageSize.getWidth(), pdf.internal.pageSize.getHeight());
                            if (index < maxImages - 1) {
                                pdf.addPage();
                            }
                            resolve();
                        };
                        reader.readAsDataURL(blob);
                    } catch (error) {
                        console.error('处理图片时出错:', error);
                        reject(error);
                    }
                },
                onerror: function (error) {
                    console.error('下载图片失败:', error);
                    reject(error);
                }
            });
        });
    }


    // 创建下载按钮和输入框
    createDownloadButton();
})();