网页漫画下载为pdf格式

将网页漫画下载为pdf方便阅读,目前仅适用于如漫画[http://www.rumanhua1.com/]

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         网页漫画下载为pdf格式
// @namespace    http://tampermonkey.net/
// @version      3.0.0
// @description  将网页漫画下载为pdf方便阅读,目前仅适用于如漫画[http://www.rumanhua1.com/]
// @author       MornLight
// @match        http://m.rumanhua1.com/*
// @match        http://www.rumanhua1.com/*
// @match        https://www.rumanhua.org/*
// @match        https://m.rumanhua.org/*
// @match        https://mangapark.net/*
// @match        https://www.mwdd.cc/*
// @match        https://www.mwhh.cc/*
// @match        https://www.mhtmh.org/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=greasyfork.org
// @grant        GM_xmlhttpRequest
// @grant        GM_openInTab
// @grant        GM_setValue
// @grant        GM_getValue
// @connect      *
// @require      https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js
// @run-at       document-end
// @license      MIT
// @supportURL   https://github.com/duanmorningsir/ComicDownloader
// ==/UserScript==


(function () {
    'use strict';
    // 禁用图片加载的功能
    function disableImageLoading() {
        console.log('🚫 批量下载模式:禁用图片实际加载');

        // 方法1: 使用CSS隐藏图片(不影响获取src属性)
        if (!document.getElementById('batch-download-no-images')) {
            const style = document.createElement('style');
            style.id = 'batch-download-no-images';
            style.textContent = `
            img { 
                content: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7") !important;
            }
        `;
            document.head.appendChild(style);
        }
    }

    function enableImageLoading() {
        console.log('✓ 恢复图片加载');
        const style = document.getElementById('batch-download-no-images');
        if (style) {
            style.remove();
        }
    }

    // 1. 样式配置
    const STYLES = {
        container: {
            position: 'fixed',
            bottom: '20px',
            right: '20px',
            zIndex: '9999',
            display: 'flex',
            flexDirection: 'column',
            gap: '16px',
            backgroundColor: '#f8fafc',
            padding: '20px',
            borderRadius: '18px',
            boxShadow: '0 4px 24px 0 rgba(60,60,100,0.13)',
            maxHeight: '80vh',
            overflowY: 'auto',
            minWidth: '260px',
            border: '1px solid #e0e6ef',
            boxSizing: 'border-box',
            '@media (max-width: 768px)': {
                bottom: '8px',
                right: '8px',
                left: '8px',
                minWidth: 'auto',
                width: 'calc(100% - 16px)',
                padding: '8px',
                gap: '6px',
                borderRadius: '10px',
            },
            backdropFilter: 'blur(10px)',
            transition: 'all 0.3s ease',
        },
        button: {
            padding: '8px 0',
            color: '#fff',
            border: 'none', // 确保没有黑框
            borderRadius: '12px',
            cursor: 'pointer',
            backgroundColor: '#4CAF50',
            boxShadow: '0 1px 4px 0 rgba(76,175,80,0.08)',
            fontWeight: 'bold',
            fontSize: '15px',
            letterSpacing: '0.5px',
            transition: 'background 0.2s, box-shadow 0.15s, transform 0.08s',
            margin: '4px 0',
            outline: 'none',
            width: '100%',
            minHeight: '36px',
            background: 'linear-gradient(45deg, #4CAF50, #45a049)',
            transition: 'all 0.3s ease',
            ':hover': {
                background: 'linear-gradient(45deg, #45a049, #4CAF50)',
                transform: 'translateY(-2px)',
                boxShadow: '0 4px 15px rgba(76,175,80,0.3)'
            },
            '@media (max-width: 768px)': {
                padding: '6px 0', // 统一padding
                fontSize: '13px',
                borderRadius: '8px',
                margin: '3px 0', // 统一margin
                minHeight: '28px', // 统一高度
                fontWeight: 'bold'
            }
        },
        cancelButton: {
            backgroundColor: '#f44336',
            fontWeight: 'bold',
            fontSize: '15px',
            borderRadius: '12px',
            boxShadow: '0 1px 4px 0 rgba(244,67,54,0.08)',
            margin: '4px 0',
            width: '100%',
            minHeight: '36px',
            border: 'none', // 添加这行,移除黑框
            padding: '8px 0', // 添加这行,统一padding
            color: '#fff',
            cursor: 'pointer',
            transition: 'all 0.3s ease',
            ':hover': {
                background: 'linear-gradient(45deg, #e53935, #f44336)',
                transform: 'translateY(-2px)',
                boxShadow: '0 4px 15px rgba(244,67,54,0.3)'
            },
            '@media (max-width: 768px)': {
                fontSize: '13px',
                borderRadius: '8px',
                margin: '3px 0',
                minHeight: '28px',
                padding: '6px 0', // 移动端padding
            },
            background: 'linear-gradient(45deg, #f44336, #e53935)'
        },
        buttonGroup: {
            display: 'flex',
            flexDirection: 'column',
            gap: '8px',
            alignItems: 'center',
            marginBottom: '10px',
            width: '100%',
        },
        progressContainer: {
            display: 'none',
            background: 'rgba(245,247,250,0.85)',
            borderRadius: '10px',
            padding: '8px 0',
            margin: '4px 0',
            boxShadow: '0 1px 4px 0 rgba(60,60,100,0.06)',
            '@media (max-width: 768px)': {
                width: '100%',
                padding: '6px 0',
                borderRadius: '7px',
            },
            backdropFilter: 'blur(5px)',
            border: '1px solid rgba(255,255,255,0.1)'
        },
        infoText: {
            color: '#4a5568',
            fontSize: '15px',
            textAlign: 'center',
            marginBottom: '10px',
            fontWeight: '500',
            letterSpacing: '0.5px',
            '@media (max-width: 768px)': {
                fontSize: '12px',
                marginBottom: '5px',
            }
        },
        chapterListContainer: {
            marginTop: '10px',
            display: 'none',
            maxHeight: '50vh',
            overflowY: 'auto',
            paddingRight: '18px',
            boxSizing: 'border-box',
            '@media (max-width: 768px)': {
                maxHeight: '60vh',
                paddingRight: '0',
            }
        }
    };

    // 添加响应式样式应用函数
    function applyResponsiveStyles(element, styles) {
        Object.assign(element.style, styles);

        // 检查是否是移动设备
        if (window.innerWidth <= 768) {
            const mobileStyles = styles['@media (max-width: 768px)'];
            if (mobileStyles) {
                Object.assign(element.style, mobileStyles);
            }
        }
    }

    // 2. 站点适配器相关代码
    class SiteAdapter {
        isChapterPage() {
            throw new Error('必须实现 isChapterPage 方法');
        }

        isDirectoryPage() {
            throw new Error('必须实现 isDirectoryPage 方法');
        }

        getChapterLinks() {
            throw new Error('必须实现 getChapterLinks 方法');
        }

        getChapterName() {
            throw new Error('必须实现 getChapterName 方法');
        }

        getImageElements() {
            throw new Error('必须实现 getImageElements 方法');
        }

        getImageUrl(imgElement) {
            throw new Error('必须实现 getImageUrl 方法');
        }

        // ✅ 新增:检查当前页面是否有分页
        hasMultiplePages() {
            return false; // 默认无分页
        }

        // ✅ 新增:获取所有分页URL(包括当前页)
        getPageUrls() {
            return [window.location.href]; // 默认只有当前页
        }

        // ✅ 新增:从指定URL获取图片URL列表(用于后台加载分页)
        async fetchImageUrlsFromPage(pageUrl) {
            console.log(`后台加载分页: ${pageUrl}`);

            return new Promise((resolve, reject) => {
                const iframe = document.createElement('iframe');
                iframe.style.display = 'none';
                document.body.appendChild(iframe);

                let timeoutId = setTimeout(() => {
                    cleanup();
                    reject(new Error('分页加载超时'));
                }, 30000);

                const cleanup = () => {
                    clearTimeout(timeoutId);
                    if (iframe.parentNode) {
                        document.body.removeChild(iframe);
                    }
                };

                iframe.onload = async () => {
                    try {
                        const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;

                        // 等待图片元素加载
                        await new Promise(wait => setTimeout(wait, 2000));

                        // 使用子类的 getImageElements 逻辑
                        const imageElements = iframeDoc.querySelectorAll(this.getImageSelector());
                        console.log(`分页找到 ${imageElements.length} 张图片`);

                        const imageUrls = [];
                        for (let img of imageElements) {
                            // 复用 getImageUrl 逻辑
                            const url = this.getImageUrlFromElement(img);
                            if (url) {
                                imageUrls.push(url);
                            }
                        }

                        cleanup();
                        resolve(imageUrls);
                    } catch (error) {
                        cleanup();
                        reject(error);
                    }
                };

                iframe.onerror = () => {
                    cleanup();
                    reject(new Error('分页加载失败'));
                };

                iframe.src = pageUrl;
            });
        }

        // ✅ 新增:获取图片选择器(子类可覆盖)
        getImageSelector() {
            return 'img'; // 默认选择器
        }

        // ✅ 新增:从元素获取图片URL(复用 getImageUrl 逻辑)
        getImageUrlFromElement(imgElement) {
            return this.getImageUrl(imgElement);
        }
    }

    class RumanhuaAdapter extends SiteAdapter {
        isChapterPage() {
            const url = window.location.href;
            const chapterPagePattern = /http:\/\/m\.rumanhua1\.com\/[^\/]+\/[^\/]+\.html/;
            return chapterPagePattern.test(url);
        }

        isDirectoryPage() {
            const url = window.location.href;
            const directoryPagePattern = /http:\/\/m\.rumanhua1\.com\/[^\/]+\/?$/;
            return directoryPagePattern.test(url);
        }

        async getChapterLinks() {
            const waitForChapterList = () => {
                return new Promise((resolve, reject) => {
                    let attempts = 0;
                    const maxAttempts = 10;

                    const checkForList = () => {
                        const selectors = [
                            '.cartoon-box .chaplist-box ul',
                            '.chaplist-box ul',
                            '.chapterlistload ul',
                            '.chapter-list ul',
                            '.chapterlist ul'
                        ];

                        for (const selector of selectors) {
                            const element = document.querySelector(selector);
                            if (element) {
                                resolve(element);
                                return;
                            }
                        }

                        attempts++;
                        if (attempts >= maxAttempts) {
                            reject(new Error('未找到章节列表'));
                            return;
                        }

                        setTimeout(checkForList, 500);
                    };

                    checkForList();
                });
            };

            try {
                const chapterListElement = await waitForChapterList();
                const chapterElements = chapterListElement.querySelectorAll('a');
                const baseUrl = window.location.origin;
                const links = Array.from(chapterElements).map(element => {
                    const href = element.getAttribute('href');
                    const url = href.startsWith('http') ? href : baseUrl + href;
                    const name = element.textContent.trim();
                    return { url, name };
                });
                return links;
            } catch (error) {
                throw error;
            }
        }

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

        getImageElements() {
            return document.querySelectorAll('.chapter-img-box img');
        }

        getImageUrl(imgElement) {
            if (!imgElement) return null;

            const src = imgElement.src || imgElement.dataset.src;
            if (!src) return null;

            let imageUrl = src.includes('/static/images/load.gif') ? imgElement.dataset.src : src;

            if (imageUrl.startsWith('blob:')) {
                return imageUrl;
            }

            if (imageUrl.startsWith('http:')) {
                imageUrl = imageUrl.replace('http:', 'https:');
            }

            return imageUrl;
        }
    }

    class RumanhuaPCAdapter extends SiteAdapter {
        isChapterPage() {
            const url = window.location.href;
            const chapterPagePattern = /http:\/\/www\.rumanhua1\.com\/[^\/]+\/[^\/]+\.html/;
            return chapterPagePattern.test(url);
        }

        isDirectoryPage() {
            const url = window.location.href;
            return url.includes('www.rumanhua1.com/') && !this.isChapterPage();
        }

        async getChapterLinks() {
            const waitForChapterList = () => {
                return new Promise((resolve, reject) => {
                    let attempts = 0;
                    const maxAttempts = 10;

                    const checkForList = () => {
                        const selectors = [
                            '.chapterlistload ul',
                            '.cartoon-box .chaplist-box ul',
                            '.chaplist-box ul',
                            '.chapter-list ul',
                            '.chapterlist ul',
                            '.chapterlistload ul li a'
                        ];
                        for (const selector of selectors) {
                            const element = document.querySelector(selector);
                            if (element) {
                                resolve(element);
                                return;
                            }
                        }
                        attempts++;
                        if (attempts >= maxAttempts) {
                            reject(new Error('未找到章节列表'));
                            return;
                        }
                        setTimeout(checkForList, 500);
                    };
                    checkForList();
                });
            };

            try {
                const chapterListElement = await waitForChapterList();
                const chapterElements = chapterListElement.querySelectorAll('a');
                const baseUrl = window.location.origin;
                const links = Array.from(chapterElements).map(element => {
                    const href = element.getAttribute('href');
                    const url = href.startsWith('http') ? href : baseUrl + href;
                    const name = element.textContent.trim();
                    return { url, name };
                });
                return links;
            } catch (error) {
                console.error('获取章节列表失败:', error);
                return [];
            }
        }

        getChapterName() {
            const chapterName = document.querySelector('.headwrap .chaptername_title')?.textContent || '未知章节';
            return chapterName;
        }

        getImageElements() {
            return document.querySelectorAll('div.chapter-img-box img');
        }

        getImageUrl(imgElement) {
            if (!imgElement) return null;

            const src = imgElement.src || imgElement.dataset.src;
            if (!src) return null;

            let imageUrl = src.includes('/static/images/load.gif') ? imgElement.dataset.src : src;

            if (imageUrl.startsWith('blob:')) {
                return imageUrl;
            }

            if (imageUrl.startsWith('http:')) {
                imageUrl = imageUrl.replace('http:', 'https:');
            }

            return imageUrl;
        }
    }

    class RumanhuaOrgAdapter extends SiteAdapter {
        isChapterPage() {
            const url = window.location.href;
            // 修改正则表达式以匹配实际的链接格式: https://www.rumanhua.org/show/xxx.html
            const chapterPagePattern = /https:\/\/www\.rumanhua\.org\/show\/[^\/]+\.html/;
            return chapterPagePattern.test(url);
        }

        isDirectoryPage() {
            const url = window.location.href;
            return url.includes('https://www.rumanhua.org') && !this.isChapterPage();
        }

        async getChapterLinks() {
            const waitForChapterList = () => {
                return new Promise((resolve, reject) => {
                    let attempts = 0;
                    const maxAttempts = 10;

                    const checkForList = () => {
                        // 修改选择器以匹配新的HTML结构
                        const selectors = [
                            'div.list a.ib',
                            '.chapterlistload ul',
                            '.cartoon-box .chaplist-box ul',
                            '.chaplist-box ul',
                            '.chapter-list ul',
                            '.chapterlist ul',
                            '.chapterlistload ul li a'
                        ];
                        for (const selector of selectors) {
                            const elements = document.querySelectorAll(selector);
                            if (elements.length > 0) {
                                resolve(elements);
                                return;
                            }
                        }
                        attempts++;
                        if (attempts >= maxAttempts) {
                            reject(new Error('未找到章节列表'));
                            return;
                        }
                        setTimeout(checkForList, 500);
                    };
                    checkForList();
                });
            };

            try {
                const chapterElements = await waitForChapterList();
                const baseUrl = window.location.origin;
                const links = Array.from(chapterElements).map(element => {
                    const href = element.getAttribute('href');
                    const url = href.startsWith('http') ? href : baseUrl + href;
                    const name = element.textContent.trim();
                    return { url, name };
                });
                return links;
            } catch (error) {
                console.error('获取章节列表失败:', error);
                return [];
            }
        }

        getChapterName() {
            // 从<title>标签中提取章节名称
            const title = document.querySelector('title')?.textContent || '未知章节';
            // 提取标题中的章节部分,例如从"龙战在野漫画-第9话 相伴左右在线阅读-如漫画"中提取"第9话 相伴左右"
            const match = title.match(/-\s*(.+?)\s*在线阅读/);
            return match ? match[1] : title;
        }

        getImageElements() {
            // 修改为根据新的图片结构获取图片元素
            return document.querySelectorAll('ul.comic-contain li img');
        }

        getImageUrl(imgElement) {
            if (!imgElement) return null;

            // 优先使用data-src属性,如果没有则使用src属性
            const src = imgElement.dataset.src || imgElement.src;
            if (!src) return null;

            // 处理图片URL
            let imageUrl = src;

            if (imageUrl.startsWith('blob:')) {
                return imageUrl;
            }

            if (imageUrl.startsWith('http:')) {
                imageUrl = imageUrl.replace('http:', 'https:');
            }

            return imageUrl;
        }
    }

    // 在 RumanhuaOrgAdapter 之后添加新的适配器类

    class RumanhuaMobileNewAdapter extends SiteAdapter {
        isChapterPage() {
            const url = window.location.href;
            // 匹配 https://m.rumanhua.org/show/xxxxx.html 格式
            const chapterPagePattern = /https:\/\/m\.rumanhua\.org\/show\/[^\/]+\.html/;
            return chapterPagePattern.test(url);
        }

        isDirectoryPage() {
            const url = window.location.href;
            // 匹配 https://m.rumanhua.org/news/xxxxxx 格式
            const directoryPagePattern = /https:\/\/m\.rumanhua\.org\/news\/\d+/;
            return directoryPagePattern.test(url);
        }

        async getChapterLinks() {
            const waitForChapterList = () => {
                return new Promise((resolve, reject) => {
                    let attempts = 0;
                    const maxAttempts = 10;

                    const checkForList = () => {
                        // 根据你提供的HTML结构,章节列表在 ul.chapterList 中
                        const selectors = [
                            'ul.chapterList li a',
                            '.chapterList a',
                            'ul.am-thumbnails li a',
                            '.chapter-list a'
                        ];

                        for (const selector of selectors) {
                            const elements = document.querySelectorAll(selector);
                            if (elements.length > 0) {
                                resolve(elements);
                                return;
                            }
                        }

                        attempts++;
                        if (attempts >= maxAttempts) {
                            reject(new Error('未找到章节列表'));
                            return;
                        }

                        setTimeout(checkForList, 500);
                    };

                    checkForList();
                });
            };

            try {
                const chapterElements = await waitForChapterList();
                const baseUrl = window.location.origin;
                const links = Array.from(chapterElements).map(element => {
                    const href = element.getAttribute('href');
                    // 处理相对路径
                    const url = href.startsWith('http') ? href : baseUrl + href;
                    const name = element.textContent.trim();
                    return { url, name };
                });
                return links;
            } catch (error) {
                console.error('获取章节列表失败:', error);
                return [];
            }
        }

        getChapterName() {
            // 从 <span id="title"> 标签中提取章节名称
            const titleSpan = document.querySelector('span#title');
            if (titleSpan) {
                const name = titleSpan.textContent.trim();
                console.log('获取章节名称:', name);
                return name;
            }
        }

        getImageElements() {
            // 根据网站结构获取漫画图片
            // 尝试多个可能的选择器
            const selectors = [
                'div.reader-img img',
                'div.comic-contain img',
                'div[class*="comic"] img',
                'div.chapter-img-box img',
                'img[data-src]',
                'img.lazy'
            ];

            for (const selector of selectors) {
                const elements = document.querySelectorAll(selector);
                if (elements.length > 0) {
                    console.log(`使用选择器: ${selector}, 找到 ${elements.length} 张图片`);
                    return elements;
                }
            }

            // 如果上面都找不到,返回所有非装饰性的img标签
            console.warn('使用备用选择器获取图片');
            return document.querySelectorAll('img[src]:not([src*="logo"]):not([src*="icon"])');
        }

        getImageUrl(imgElement) {
            if (!imgElement) return null;

            // 优先使用 data-src(懒加载),其次是 src
            let imageUrl = imgElement.dataset.src || imgElement.src;

            if (!imageUrl) return null;

            // 处理特殊情况
            if (imageUrl.includes('placeholder') || imageUrl.includes('loading')) {
                return null;
            }

            // 如果是blob URL,直接返回
            if (imageUrl.startsWith('blob:')) {
                return imageUrl;
            }

            // 转换为HTTPS
            if (imageUrl.startsWith('http:')) {
                imageUrl = imageUrl.replace('http:', 'https:');
            }

            // 处理相对URL
            if (imageUrl.startsWith('//')) {
                imageUrl = 'https:' + imageUrl;
            } else if (imageUrl.startsWith('/') && !imageUrl.startsWith('//')) {
                imageUrl = window.location.origin + imageUrl;
            }

            return imageUrl;
        }
    }

    // 添加Mangapark适配器
    class MangaparkAdapter extends SiteAdapter {
        isChapterPage() {
            // 匹配 https://mangapark.net/title/357480-en-the-31st-piece-turns-the-tables/9864935-chapter-85-season-3-start 类型URL
            const chapterPagePattern = /https:\/\/mangapark\.net\/title\/+[^\/]+\/+[^\/]/;
            return chapterPagePattern.test(window.location.href);
        }

        isDirectoryPage() {
            // 目录页匹配 /title/ 开头但不包含 /chapter- 的URL
            const url = window.location.href;
            return url.includes('https://mangapark.net/title/') && !this.isChapterPage();
        }

        async getChapterLinks() {
            const waitForChapterList = () => {
                return new Promise((resolve, reject) => {
                    let attempts = 0;
                    const maxAttempts = 10;

                    const checkForList = () => {
                        // Mangapark的章节列表选择器
                        const selectors = [
                            'div[data-name="chapter-list"] div.scrollable-panel div.group div.px-2 > div.space-x-1 a',
                        ];

                        for (const selector of selectors) {
                            const elements = document.querySelectorAll(selector);
                            if (elements.length > 0) {
                                resolve(elements);
                                return;
                            }
                        }

                        attempts++;
                        if (attempts >= maxAttempts) {
                            reject(new Error('未找到章节列表'));
                            return;
                        }

                        setTimeout(checkForList, 500);
                    };

                    checkForList();
                });
            };

            try {
                const chapterElements = await waitForChapterList();
                const links = Array.from(chapterElements).map(element => {
                    const href = element.getAttribute('href');
                    // 确保URL是完整的
                    const url = href.startsWith('http') ? href : 'https://mangapark.net' + href;
                    const name = element.textContent.trim();
                    return { url, name };
                });
                // 按照章节顺序排序
                return links.reverse(); // Mangapark列表通常是倒序的
            } catch (error) {
                console.error('获取章节列表失败:', error);
                return [];
            }
        }

        getChapterName() {
            // 尝试多种选择器获取章节名
            const selectors = [
                'div.text-base-content h6.text-lg span'
            ];

            for (const selector of selectors) {
                const element = document.querySelector(selector);
                if (element) {
                    return element.textContent.trim();
                }
            }

            // 如果找不到,从URL中提取
            const urlParts = window.location.pathname.split('/');
            const chapterPart = urlParts[urlParts.length - 1];
            return chapterPart.replace(/-\d+-/, ' ').replace(/-/g, ' ');
        }

        getImageElements() {
            // 查找所有带有background-image属性的div元素
            return document.querySelectorAll('div[data-name="image-show"] img, div[data-name="image-item"] img');
        }

        getImageUrl(imgElement) {
            if (!imgElement) return null;

            // 优先使用data-src或data-url属性,然后是src
            const src = imgElement.dataset.src || imgElement.dataset.url || imgElement.src;
            if (src) {
                let imageUrl = src;

                // 处理相对URL
                if (imageUrl.startsWith('//')) {
                    imageUrl = 'https:' + imageUrl;
                } else if (imageUrl.startsWith('/')) {
                    imageUrl = 'https://mangapark.net' + imageUrl;
                }

                if (imageUrl.startsWith('blob:')) {
                    return imageUrl;
                }

                return imageUrl;
            }
        }
    }
    // 在 MangaparkAdapter 之后添加新的适配器类
    class ManwakuAdapter extends SiteAdapter {
        isChapterPage() {
            const url = window.location.href;
            // 匹配 https://www.mwdd.cc/comic/xxxxx/xxxxx 格式
            const chapterPagePattern = /https?:\/\/(www\.)?(mwdd|mwhh|mhtmh)\.(cc|org)\/comic\/\d+\/\d+\/?$/;
            return chapterPagePattern.test(url);
        }

        isDirectoryPage() {
            const url = window.location.href;
            // 匹配 https://www.mwdd.cc/comic/xxxxx/ 格式(只有漫画ID,没有章节ID)
            const directoryPagePattern = /https?:\/\/(www\.)?(mwdd|mwhh|mhtmh)\.(cc|org)\/comic\/\d+\/?$/;
            return directoryPagePattern.test(url) && !this.isChapterPage();
        }

        async getChapterLinks() {
            const waitForChapterList = () => {
                return new Promise((resolve, reject) => {
                    let attempts = 0;
                    const maxAttempts = 10;

                    const checkForList = () => {
                        // 根据提供的HTML结构,章节列表在 div.chapter-grid 中
                        // 每个章节是一个 a.chapter-item 元素
                        const selectors = [
                            'div.chapter-grid a.chapter-item',
                            'div#chapter-grid-container a.chapter-item',
                            '.chapter-grid a.chapter-item',
                            'a.chapter-item'
                        ];

                        for (const selector of selectors) {
                            const elements = document.querySelectorAll(selector);
                            if (elements.length > 0) {
                                console.log(`找到 ${elements.length} 个章节链接,使用选择器: ${selector}`);
                                resolve(elements);
                                return;
                            }
                        }

                        attempts++;
                        if (attempts >= maxAttempts) {
                            reject(new Error('未找到章节列表'));
                            return;
                        }

                        setTimeout(checkForList, 500);
                    };

                    checkForList();
                });
            };

            try {
                const chapterElements = await waitForChapterList();
                const baseUrl = window.location.origin;
                const links = Array.from(chapterElements).map(element => {
                    const href = element.getAttribute('href');
                    const url = href.startsWith('http') ? href : baseUrl + href;

                    // 从 data-title 属性获取章节名称,备选方案是从 .chapter-name 获取
                    let name = element.getAttribute('data-title');
                    if (!name) {
                        const nameEl = element.querySelector('.chapter-name');
                        name = nameEl ? nameEl.textContent.trim() : '未知章节';
                    }

                    return { url, name };
                });

                console.log(`获取到 ${links.length} 个章节`);
                return links;
            } catch (error) {
                console.error('获取章节列表失败:', error);
                return [];
            }
        }

        // ✅ 实现:检查是否有分页
        hasMultiplePages() {
            const titleSpan = document.querySelector('span.basetitle#current-title');
            if (titleSpan) {
                const name = titleSpan.textContent.trim();
                return /\(第\d+\/\d+页\)/.test(name);
            }
            return false;
        }

        // ✅ 实现:获取所有分页URL
        getPageUrls() {
            const titleSpan = document.querySelector('span.basetitle#current-title');
            if (!titleSpan) {
                return [window.location.href];
            }

            const name = titleSpan.textContent.trim();
            const match = name.match(/\(第(\d+)\/(\d+)页\)/);

            if (!match) {
                return [window.location.href];
            }

            const totalPages = parseInt(match[2]);
            const baseUrl = window.location.href.split('_')[0];

            const pageUrls = [];
            for (let i = 1; i <= totalPages; i++) {
                if (i === 1) {
                    pageUrls.push(baseUrl);
                } else {
                    pageUrls.push(`${baseUrl}_${i}`);
                }
            }

            console.log(`检测到 ${totalPages} 个分页:`, pageUrls);
            return pageUrls;
        }

        // ✅ 实现:图片选择器
        getImageSelector() {
            return 'article.epContent figure.cImg img, figure.cImg img';
        }

        getChapterName() {
            const titleSpan = document.querySelector('span.basetitle#current-title');
            if (titleSpan) {
                const name = titleSpan.textContent.trim();
                // 移除分页标记
                return name.replace(/\s*\(第\d+\/\d+页\)/, '');
            }

            const title = document.querySelector('title')?.textContent || '未知章节';
            return title;
        }

        getImageElements() {
            // 根据 HTML 结构,图片在 <figure class="cImg"> 中的 <img> 标签
            const selectors = [
                'article.epContent figure.cImg img',
                'div#showimgcontent figure.cImg img',
                'figure.cImg img'
            ];

            for (const selector of selectors) {
                const elements = document.querySelectorAll(selector);
                if (elements.length > 0) {
                    console.log(`使用选择器: ${selector}, 找到 ${elements.length} 张图片`);
                    return elements;
                }
            }

            console.warn('使用备用选择器获取图片');
            return document.querySelectorAll('img');
        }

        getImageUrl(imgElement) {
            if (!imgElement) return null;

            // 优先使用 src 属性(这个网站直接提供了完整的图片URL)
            let imageUrl = imgElement.src;

            // ✅ 校验:如果 src 是占位图(loading.gif),则使用 dataset.src
            if (imageUrl && imageUrl.includes('loading.gif')) {
                console.log('检测到占位图,使用 dataset.src:', imgElement.dataset.src);
                imageUrl = imgElement.dataset.src;
            }

            if (!imageUrl) {
                // 备选:尝试 data-src
                imageUrl = imgElement.dataset.src;
            }

            if (!imageUrl) return null;

            // 处理特殊情况
            if (imageUrl.includes('placeholder') || imageUrl.includes('loading')) {
                return null;
            }

            // 如果是blob URL,直接返回
            if (imageUrl.startsWith('blob:')) {
                return imageUrl;
            }

            // 转换为HTTPS
            if (imageUrl.startsWith('http:')) {
                imageUrl = imageUrl.replace('http:', 'https:');
            }

            // 处理相对URL
            if (imageUrl.startsWith('//')) {
                imageUrl = 'https:' + imageUrl;
            } else if (imageUrl.startsWith('/') && !imageUrl.startsWith('//')) {
                imageUrl = window.location.origin + imageUrl;
            }

            console.log('获取的图片URL:', imageUrl);
            return imageUrl;
        }
    }

    // 3. 获取适配器的工厂函数
    function getSiteAdapter() {
        const url = window.location.href;
        switch (true) {
            case url.includes('http://www.rumanhua1.com/'):
                return new RumanhuaPCAdapter();
            case url.includes('http://m.rumanhua1.com/'):
                return new RumanhuaAdapter();
            case url.includes('https://www.rumanhua.org/'):
                return new RumanhuaOrgAdapter();
            case url.includes('https://m.rumanhua.org/'):
                return new RumanhuaMobileNewAdapter();
            case url.includes('https://mangapark.net/'):
                return new MangaparkAdapter();
            case url.includes('mwdd.cc') || url.includes('mwhh.cc') || url.includes('mhtmh.org'):
                return new ManwakuAdapter();
            default:
                throw new Error('不支持的页面格式');
        }
    }

    // 4. UI类
    // 4.1 普通下载器UI
    class DownloaderUI {
        constructor(totalPages, onDownload, onCancel) {
            this.totalPages = totalPages;
            this.onDownload = onDownload;
            this.onCancel = onCancel;
            this.currentPage = 0;
            this.createUI();
        }

        createUI() {
            // 先创建容器
            this.container = this.createContainer();
            document.body.appendChild(this.container);

            // 添加页数信息
            this.infoText = this.createElement('div', STYLES.infoText, `本章节共 ${this.totalPages} 页`);
            this.container.appendChild(this.infoText);

            // 添加滚动阅读模式切换按钮
            this.scrollModeButton = this.createElement('button', {
                ...STYLES.button,
                backgroundColor: '#2196F3',
                marginBottom: '10px'
            }, '切换滚动阅读模式');
            this.scrollModeButton.addEventListener('click', () => this.toggleScrollMode());
            this.container.appendChild(this.scrollModeButton);


            this.downloadButton = this.createButton('下载本章节', () => this.onDownload(1, this.totalPages));

            this.cancelButton = this.createButton('取消下载', () => {
                this.onCancel();
                this.infoText.textContent = '下载已取消';
                setTimeout(() => {
                    this.infoText.textContent = `本章节共 ${this.totalPages} 页`;
                }, 2000);
            }, true);

            // 默认隐藏取消按钮
            this.cancelButton.style.display = 'none';

            // 创建进度容器
            this.progressContainer = this.createElement('div', {
                display: 'none',
                marginTop: '10px',
                padding: '6px',
                backgroundColor: 'rgba(245,247,250,0.9)',
                borderRadius: '8px',
                '@media (max-width: 768px)': {
                    padding: '4px'
                }
            });

            this.progressBar = document.createElement('progress');
            this.progressBar.max = this.totalPages;
            this.progressBar.value = 0;
            this.progressBar.style.width = '100%';
            this.progressBar.style.height = '4px';
            this.progressBar.style.borderRadius = '2px';
            this.progressBar.style.border = 'none';
            this.progressBar.style.backgroundColor = '#e0e0e0';

            this.progressText = this.createElement('div', {
                marginTop: '6px',
                fontSize: '12px',
                color: '#333',
                '@media (max-width: 768px)': {
                    fontSize: '11px',
                    marginTop: '4px'
                }
            });

            this.progressContainer.appendChild(this.progressBar);
            this.progressContainer.appendChild(this.progressText);

            // 按顺序添加所有元素到容器
            this.container.appendChild(this.downloadButton);
            this.container.appendChild(this.cancelButton);
            this.container.appendChild(this.progressContainer);

            // 添加窗口大小变化监听
            window.addEventListener('resize', () => {
                this.updateResponsiveStyles();
            });
        }

        setLoading(isLoading, showCancel = false) {
            this.downloadButton.disabled = isLoading;
            this.downloadButton.style.backgroundColor = isLoading ? '#999' : '#4CAF50';
            this.downloadButton.style.cursor = isLoading ? 'not-allowed' : 'pointer';
            this.downloadButton.textContent = isLoading ? '下载中...' : '下载本章节';

            if (isLoading) {
                this.downloadButton.style.display = 'none';
                this.cancelButton.style.display = showCancel ? 'block' : 'none';
                // ✅ 隐藏进度条容器
                this.progressContainer.style.display = 'none';
                this.infoText.style.display = 'block';
                this.scrollModeButton.style.display = 'none';
            } else {
                this.downloadButton.style.display = 'block';
                this.cancelButton.style.display = 'none';
                this.progressContainer.style.display = 'none';
                this.infoText.style.display = 'block';
                this.scrollModeButton.style.display = 'block';
            }
        }


        updateProgress(currentPage) {
            this.currentPage = currentPage;
            this.progressBar.value = currentPage;
            const percent = ((currentPage / this.totalPages) * 100).toFixed(1);

            // ✅ 修改:只显示文字提示,不显示进度条
            this.infoText.textContent = `📥 下载中... ${currentPage}/${this.totalPages}`;
            this.infoText.style.display = 'block';
            this.infoText.style.color = '#2196F3';
        }

        createButton(text, onClick, isCancel = false) {
            const button = document.createElement('button');
            Object.assign(button.style, STYLES.button); // 应用基础按钮样式
            if (isCancel) {
                Object.assign(button.style, STYLES.cancelButton); // 如果是取消按钮,额外应用取消按钮样式
            }
            button.textContent = text;
            button.addEventListener('click', onClick);
            return button;
        }

        createContainer() {
            const container = document.createElement('div');
            Object.assign(container.style, STYLES.container);
            return container;
        }

        createProgressBar() {
            const progressBar = document.createElement('progress');
            progressBar.max = this.totalPages;
            progressBar.value = 0;
            progressBar.style.width = '100%';
            return progressBar;
        }

        toggleScrollMode() {
            this.isScrollMode = !this.isScrollMode;
            this.scrollModeButton.textContent = this.isScrollMode ? '切换普通模式' : '切换滚动阅读模式';
            this.scrollModeButton.style.backgroundColor = this.isScrollMode ? '#4CAF50' : '#2196F3';
        }


        // 修改 createElement 方法
        createElement(type, styles, textContent = '') {
            const element = document.createElement(type);
            if (type === 'input' && styles.type) {
                setLoading;
                element.type = styles.type;
                delete styles.type;
            }
            if (typeof styles === 'string') {
                element.className = styles;
            } else {
                applyResponsiveStyles(element, styles);
            }
            if (textContent) element.textContent = textContent;
            return element;
        }
    }

    // 4.2 章节选择器UI
    class ChapterSelectorUI {
        constructor({ adapter, onDownloadSelected, ontoggleScrollMode, onCancel, onProgress, onLoading, onComplete, onError }) {
            this.adapter = adapter;
            this.onDownloadSelected = onDownloadSelected;
            this.ontoggleScrollMode = ontoggleScrollMode;
            this.onCancel = onCancel;
            this.onProgress = onProgress;
            this.onLoading = onLoading;
            this.onComplete = onComplete;
            this.onError = onError;
            this.selectedChapters = new Set();
            this.isSelectionMode = false;
            this.isScrollMode = false;
            this.selectionStart = null;
            this.selectionEnd = null;
            this.isDownloading = false; // 添加下载状态标志
            this.createUI();
            this.chapterListContainer.style.display = 'block';
            this.initChapterList();
        }

        createUI() {
            // 创建容器
            this.container = this.createElement('div', {
                ...STYLES.container,
                '@media (min-width: 769px)': {
                    width: '300px',
                    right: '20px',
                    left: 'auto'
                }
            });
            document.body.appendChild(this.container);

            // ✅ 创建横向按钮容器(紧凑型)
            this.buttonRow = this.createElement('div', {
                display: 'flex',
                gap: '8px',
                width: '100%',
                marginBottom: '10px',
                position: 'sticky',
                top: '0',
                zIndex: '10',
                backgroundColor: 'rgba(248,250,252,0.95)',
                padding: '8px 0',
                borderRadius: '10px',
                '@media (max-width: 768px)': {
                    gap: '6px',
                    padding: '6px 0'
                }
            });

            // ✅ 创建【选择章节下载】按钮(紧凑版)
            this.selectButton = this.createElement('button', {
                flex: '1',
                padding: '10px 8px',
                fontSize: '13px',
                fontWeight: 'bold',
                color: '#fff',
                backgroundColor: '#4CAF50',
                border: 'none',
                borderRadius: '8px',
                cursor: 'pointer',
                transition: 'all 0.2s ease',
                boxShadow: '0 2px 4px rgba(76,175,80,0.2)',
                display: 'flex',
                flexDirection: 'column',
                alignItems: 'center',
                justifyContent: 'center',
                gap: '2px',
                minHeight: '48px',
                '@media (max-width: 768px)': {
                    padding: '8px 6px',
                    fontSize: '11px',
                    minHeight: '44px'
                }
            });

            // 按钮图标
            const selectIcon = document.createElement('span');
            selectIcon.textContent = '📥';
            selectIcon.style.fontSize = '18px';
            selectIcon.style.lineHeight = '1';

            // 按钮文字
            const selectText = document.createElement('span');
            selectText.textContent = '选择下载';
            selectText.style.fontSize = '12px';
            selectText.style.lineHeight = '1';
            selectText.style.whiteSpace = 'nowrap';

            this.selectButton.appendChild(selectIcon);
            this.selectButton.appendChild(selectText);
            this.selectButtonText = selectText; // 保存引用方便更新文字

            this.selectButton.addEventListener('click', () => {
                if (this.isSelectionMode) {
                    if (this.selectedChapters.size > 0) {
                        this.onDownloadSelected && this.onDownloadSelected(Array.from(this.selectedChapters));
                    }
                } else {
                    this.toggleSelectionMode();
                }
            });

            // ✅ 创建【滚动阅读模式】按钮(紧凑版)
            this.scrollModeButton = this.createElement('button', {
                flex: '1',
                padding: '10px 8px',
                fontSize: '13px',
                fontWeight: 'bold',
                color: '#fff',
                backgroundColor: '#2196F3',
                border: 'none',
                borderRadius: '8px',
                cursor: 'pointer',
                transition: 'all 0.2s ease',
                boxShadow: '0 2px 4px rgba(33,150,243,0.2)',
                display: 'flex',
                flexDirection: 'column',
                alignItems: 'center',
                justifyContent: 'center',
                gap: '2px',
                minHeight: '48px',
                '@media (max-width: 768px)': {
                    padding: '8px 6px',
                    fontSize: '11px',
                    minHeight: '44px'
                }
            });

            const scrollIcon = document.createElement('span');
            scrollIcon.textContent = '📜';
            scrollIcon.style.fontSize = '18px';
            scrollIcon.style.lineHeight = '1';

            const scrollText = document.createElement('span');
            scrollText.textContent = '滚动';
            scrollText.style.fontSize = '12px';
            scrollText.style.lineHeight = '1';
            scrollText.style.whiteSpace = 'nowrap';

            this.scrollModeButton.appendChild(scrollIcon);
            this.scrollModeButton.appendChild(scrollText);
            this.scrollModeButtonText = scrollText;
            this.scrollModeButtonIcon = scrollIcon;

            this.scrollModeButton.addEventListener('click', () => {
                this.isScrollMode = !this.isScrollMode;
                this.scrollModeButtonText.textContent = this.isScrollMode ? '普通' : '滚动';
                this.scrollModeButtonIcon.textContent = this.isScrollMode ? '📄' : '📜';
                this.scrollModeButton.style.backgroundColor = this.isScrollMode ? '#4CAF50' : '#2196F3';
                this.onToggleScrollMode && this.onToggleScrollMode(this.isScrollMode);
            });

            // ✅ 创建【返回/取消】按钮(紧凑版)
            this.cancelSelectionButton = this.createElement('button', {
                flex: '1',
                padding: '10px 8px',
                fontSize: '13px',
                fontWeight: 'bold',
                color: '#fff',
                backgroundColor: '#f44336',
                border: 'none',
                borderRadius: '8px',
                cursor: 'pointer',
                transition: 'all 0.2s ease',
                boxShadow: '0 2px 4px rgba(244,67,54,0.2)',
                display: 'none',
                flexDirection: 'column',
                alignItems: 'center',
                justifyContent: 'center',
                gap: '2px',
                minHeight: '48px',
                '@media (max-width: 768px)': {
                    padding: '8px 6px',
                    fontSize: '11px',
                    minHeight: '44px'
                }
            });

            const cancelIcon = document.createElement('span');
            cancelIcon.textContent = '⬅️';
            cancelIcon.style.fontSize = '18px';
            cancelIcon.style.lineHeight = '1';

            const cancelText = document.createElement('span');
            cancelText.textContent = '返回';
            cancelText.style.fontSize = '12px';
            cancelText.style.lineHeight = '1';
            cancelText.style.whiteSpace = 'nowrap';

            this.cancelSelectionButton.appendChild(cancelIcon);
            this.cancelSelectionButton.appendChild(cancelText);
            this.cancelButtonText = cancelText;

            this.cancelSelectionButton.addEventListener('click', () => {
                if (this.isDownloading) {
                    this.cancelDownload();
                } else {
                    this.cancelSelectionMode();
                    this.onCancel && this.onCancel();
                }
            });

            // ✅ 将三个按钮添加到横向容器
            this.buttonRow.appendChild(this.selectButton);
            this.buttonRow.appendChild(this.scrollModeButton);
            this.buttonRow.appendChild(this.cancelSelectionButton);

            // 将横向按钮容器添加到主容器
            this.container.appendChild(this.buttonRow);

            // 创建章节列表容器(保持不变)
            this.chapterListContainer = this.createElement('div', {
                marginTop: '10px',
                display: 'none',
                maxHeight: '50vh',
                overflowY: 'auto',
                paddingRight: '4px',
                boxSizing: 'border-box',
                scrollBehavior: 'auto',
                WebkitOverflowScrolling: 'touch',
                '@media (max-width: 768px)': {
                    maxHeight: '60vh',
                    paddingRight: '8px',
                }
            });
            this.chapterListContainer.classList.add('comic-downloader-chapter-list');
            this.container.appendChild(this.chapterListContainer);

            // 添加进度显示区域(保持不变)
            this.progressContainer = this.createElement('div', {
                marginTop: '10px',
                display: 'none',
                position: 'sticky',
                bottom: '0',
                backgroundColor: 'rgba(245,247,250,0.9)',
                padding: '6px',
                borderRadius: '8px',
                zIndex: '2',
                '@media (max-width: 768px)': {
                    padding: '4px'
                }
            });

            this.progressBar = document.createElement('progress');
            this.progressBar.style.width = '100%';
            this.progressBar.style.height = '4px';
            this.progressBar.style.borderRadius = '2px';
            this.progressBar.style.border = 'none';
            this.progressBar.style.backgroundColor = '#e0e0e0';

            this.progressText = this.createElement('div', {
                marginTop: '6px',
                fontSize: '12px',
                color: '#333',
                '@media (max-width: 768px)': {
                    fontSize: '11px',
                    marginTop: '4px'
                }
            });

            this.progressContainer.appendChild(this.progressBar);
            this.progressContainer.appendChild(this.progressText);
            this.container.appendChild(this.progressContainer);
        }

        toggleSelectionMode() {
            console.log('toggleSelectionMode 被调用');
            this.isSelectionMode = !this.isSelectionMode;

            if (this.isSelectionMode) {
                console.log('进入选择模式');
                // ✅ 只在列表为空时才初始化
                if (this.chapterListContainer.children.length === 0) {
                    console.log('章节列表为空,准备初始化');
                    this.initChapterList();
                }
                this.chapterListContainer.style.display = 'block';
                this.cancelSelectionButton.style.display = 'block';
                this.selectButton.textContent = '下载选中章节';
                this.scrollModeButton.style.display = 'block';
            } else {
                console.log('退出选择模式');
                if (this.selectedChapters.size > 0) {
                    console.log(`开始下载 ${this.selectedChapters.size} 个选中章节`);
                    this.onDownloadSelected();
                } else {
                    console.log('无选中章节,返回初始状态');
                    this.chapterListContainer.style.display = 'none';
                    this.cancelSelectionButton.style.display = 'none';
                    this.selectButton.textContent = '选择章节下载';
                }
            }
        }

        cancelSelectionMode() {
            this.isSelectionMode = false;
            this.chapterListContainer.style.display = 'none';
            this.cancelSelectionButton.style.display = 'none';
            this.selectButton.textContent = '选择章节下载';
            this.selectedChapters.clear();
            this.selectionStart = null;
            this.selectionEnd = null;
            this.updateChapterSelectionUI();
        }
        cancelDownload() {
            console.log('用户取消批量下载');

            // 设置取消标志
            GM_setValue('cancelBatchDownload', true);

            // 重置UI状态
            this.isDownloading = false;
            this.setLoading(false);

            // 显示取消提示
            this.selectButton.textContent = '⏸️ 下载已取消';
            this.selectButton.style.backgroundColor = '#ff9800';
            this.selectButton.disabled = true;

            setTimeout(() => {
                this.selectButton.textContent = '选择章节下载';
                this.selectButton.style.backgroundColor = '#4CAF50';
                this.selectButton.disabled = false;
            }, 2000);
        }

        async initChapterList() {
            // ✅ 保存当前滚动位置(如果需要刷新列表)
            const previousScrollTop = this.chapterListContainer.scrollTop || 0;

            this.chapterListContainer.innerHTML = '';
            this.selectedChapters = new Set();
            this.selectionStart = null;
            this.selectionEnd = null;

            try {
                const chapterLinks = await this.adapter.getChapterLinks();
                if (!chapterLinks || chapterLinks.length === 0) {
                    return;
                }

                // 操作区
                const controlsContainer = this.createElement('div', {
                    display: 'flex',
                    justifyContent: 'space-between',
                    alignItems: 'center',
                    marginBottom: '12px',
                    gap: '8px',
                    position: 'sticky',
                    top: '0',
                    backgroundColor: 'rgba(255,255,255,0.9)',
                    padding: '5px 0',
                    zIndex: '1',
                    // ✅ 添加阴影,让sticky效果更明显
                    boxShadow: '0 2px 4px rgba(0,0,0,0.05)'
                });

                const chapterCountLabel = this.createElement('span', {
                    fontSize: '12px',
                    color: '#666',
                    alignSelf: 'center'
                }, `共 ${chapterLinks.length} 章`);

                const buttonsContainer = this.createElement('div', {
                    display: 'flex',
                    gap: '5px'
                });

                // 全选按钮
                const selectAllBtn = this.createElement('button', {
                    padding: '3px 10px',
                    fontSize: '12px',
                    backgroundColor: '#4caf50',
                    color: 'white',
                    border: 'none',
                    borderRadius: '4px',
                    cursor: 'pointer',
                    fontWeight: 'bold'
                }, '全选章节');

                selectAllBtn.addEventListener('click', () => {
                    const scrollTop = this.chapterListContainer.scrollTop;

                    this.selectedChapters = new Set(chapterLinks.map((_, i) => i));
                    this.selectionStart = 0;
                    this.selectionEnd = chapterLinks.length - 1;
                    this.updateChapterSelectionUI();
                    this.selectButtonText.textContent = `下载 (${this.selectedChapters.size})`; // ✅ 更新文字

                    this.chapterListContainer.scrollTop = scrollTop;
                });

                // 清除选择按钮
                const deselectAllBtn = this.createElement('button', {
                    padding: '3px 10px',
                    fontSize: '12px',
                    backgroundColor: '#f44336',
                    color: 'white',
                    border: 'none',
                    borderRadius: '4px',
                    cursor: 'pointer',
                    fontWeight: 'bold'
                }, '清除选择');

                deselectAllBtn.addEventListener('click', () => {
                    const scrollTop = this.chapterListContainer.scrollTop;

                    this.selectedChapters.clear();
                    this.selectionStart = null;
                    this.selectionEnd = null;
                    this.updateChapterSelectionUI();
                    this.selectButtonText.textContent = '选择下载'; // ✅ 更新文字

                    this.chapterListContainer.scrollTop = scrollTop;
                });

                // 刷新按钮
                const refreshBtn = this.createElement('button', {
                    padding: '3px 10px',
                    fontSize: '12px',
                    backgroundColor: '#2196f3',
                    color: 'white',
                    border: 'none',
                    borderRadius: '4px',
                    cursor: 'pointer',
                    fontWeight: 'bold'
                }, '刷新列表');

                refreshBtn.addEventListener('click', () => this.refreshChapterList());

                buttonsContainer.appendChild(selectAllBtn);
                buttonsContainer.appendChild(deselectAllBtn);
                buttonsContainer.appendChild(refreshBtn);
                controlsContainer.appendChild(chapterCountLabel);
                controlsContainer.appendChild(buttonsContainer);
                this.chapterListContainer.appendChild(controlsContainer);

                // 章节列表
                chapterLinks.forEach((chapter, index) => {
                    const chapterItem = this.createElement('div', {
                        padding: '10px 12px',
                        marginBottom: '8px',
                        borderRadius: '8px',
                        background: '#f5f7fa',
                        cursor: 'pointer',
                        transition: 'background 0.2s, color 0.2s',
                        fontSize: '15px',
                        fontWeight: '500',
                        userSelect: 'none',
                        border: '1px solid #e0e6ef',
                        // ✅ 添加触摸反馈
                        '@media (max-width: 768px)': {
                            padding: '12px',
                            fontSize: '14px',
                            touchAction: 'manipulation', // 优化触摸体验
                        }
                    });
                    chapterItem.textContent = chapter.name;

                    // ✅ 添加触摸反馈效果
                    chapterItem.addEventListener('touchstart', () => {
                        chapterItem.style.transform = 'scale(0.98)';
                    });
                    chapterItem.addEventListener('touchend', () => {
                        chapterItem.style.transform = 'scale(1)';
                    });

                    chapterItem.addEventListener('click', (e) => {
                        // ✅ 阻止默认行为
                        e.preventDefault();
                        e.stopPropagation();
                        this.handleChapterClick(index, chapterLinks.length);
                    });

                    chapterItem.classList.add('chapter-item');
                    this.chapterListContainer.appendChild(chapterItem);
                });

                this.updateChapterSelectionUI();

                // ✅ 恢复滚动位置(用于刷新列表时)
                if (previousScrollTop > 0) {
                    this.chapterListContainer.scrollTop = previousScrollTop;
                }
            } catch (error) {
                console.error('初始化章节列表失败:', error);
            }
        }

        handleChapterClick(index, total) {
            console.log(`处理章节点击: index=${index}, total=${total}`);

            // ✅ 保存当前滚动位置
            const scrollTop = this.chapterListContainer.scrollTop;

            // ✅ 修改:如果不在选择模式,只改变标志,不重新初始化列表
            if (!this.isSelectionMode) {
                this.isSelectionMode = true;
                this.cancelSelectionButton.style.display = 'block';
                this.selectButton.textContent = '下载选中章节';
                this.scrollModeButton.style.display = 'block';
                console.log('进入选择模式(不重新初始化列表)');
            }

            if (this.selectionStart === null) {
                // 第一次点击,设置起始点
                this.selectionStart = index;
                this.selectedChapters = new Set([index]);
            } else if (this.selectionEnd === null) {
                // 第二次点击,设置结束点并选择区间
                this.selectionEnd = index;
                const [start, end] = [this.selectionStart, this.selectionEnd].sort((a, b) => a - b);
                this.selectedChapters = new Set();
                for (let i = start; i <= end; i++) {
                    this.selectedChapters.add(i);
                }
            } else {
                // 第三次点击,开始新的选择
                this.selectionStart = index;
                this.selectionEnd = null;
                this.selectedChapters = new Set([index]);
            }

            this.updateChapterSelectionUI();

            // ✅ 恢复滚动位置
            this.chapterListContainer.scrollTop = scrollTop;

            if (this.selectedChapters.size > 0) {
                this.selectButtonText.textContent = `下载 (${this.selectedChapters.size})`; // ✅ 更新文字
            } else {
                this.selectButtonText.textContent = '选择下载';
            }
        }

        updateChapterSelectionUI() {
            console.log('更新章节选择UI');
            const items = this.chapterListContainer.querySelectorAll('.chapter-item');

            // ✅ 只修改样式,不重新创建元素
            items.forEach((item, idx) => {
                if (this.selectedChapters.has(idx)) {
                    item.style.background = '#2196f3';
                    item.style.color = '#fff';
                    item.style.fontWeight = 'bold'; // 添加加粗,更明显
                } else {
                    item.style.background = '#f5f7fa';
                    item.style.color = '#222';
                    item.style.fontWeight = '500';
                }
            });
        }

        refreshChapterList() {
            this.initChapterList();
        }

        toggleScrollMode() {
            this.isScrollMode = !this.isScrollMode;
            this.scrollModeButton.textContent = this.isScrollMode ? '切换普通模式' : '切换长图模式';
            this.scrollModeButton.style.backgroundColor = this.isScrollMode ? '#4CAF50' : '#2196F3';
        }

        createElement(type, styles, textContent = '') {
            const element = document.createElement(type);
            if (type === 'input' && styles.type) {
                element.type = styles.type;
                delete styles.type;
            }
            if (typeof styles === 'string') {
                element.className = styles;
            } else {
                applyResponsiveStyles(element, styles);
            }
            if (textContent) element.textContent = textContent;
            return element;
        }

        // 添加 setLoading 方法
        setLoading(isLoading, totalChapters = 0) {
            console.log(`设置加载状态: isLoading=${isLoading}, totalChapters=${totalChapters}`);
            this.isDownloading = isLoading;

            if (isLoading) {
                // 下载中状态
                this.selectButton.disabled = true;
                this.selectButton.style.backgroundColor = '#4CAF50';
                this.selectButton.style.cursor = 'not-allowed';
                this.selectButtonText.textContent = '下载中...'; // ✅ 更新文字

                this.scrollModeButton.style.display = 'none';

                // 取消按钮显示
                this.cancelSelectionButton.style.display = 'flex'; // ✅ 改为 flex
                this.cancelButtonText.textContent = '取消';
                this.cancelSelectionButton.style.backgroundColor = '#f44336';

                this.chapterListContainer.style.display = 'none';

                // 显示进度信息容器
                this.progressInfoContainer = this.createElement('div', {
                    marginTop: '12px',
                    padding: '12px',
                    backgroundColor: 'rgba(255,255,255,0.9)',
                    borderRadius: '10px',
                    border: '1px solid #e0e6ef',
                    boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
                    '@media (max-width: 768px)': {
                        padding: '10px',
                        marginTop: '8px'
                    }
                });

                // 章节进度
                this.chapterProgressText = this.createElement('div', {
                    fontSize: '14px',
                    color: '#2196F3',
                    fontWeight: 'bold',
                    marginBottom: '8px',
                    '@media (max-width: 768px)': {
                        fontSize: '12px',
                        marginBottom: '6px'
                    }
                }, '📖 章节进度: 0/0');

                // 当前章节名
                this.currentChapterText = this.createElement('div', {
                    fontSize: '13px',
                    color: '#555',
                    marginBottom: '8px',
                    wordBreak: 'break-word',
                    '@media (max-width: 768px)': {
                        fontSize: '11px',
                        marginBottom: '6px'
                    }
                }, '📕 当前章节: --');

                // 图片进度
                this.imageProgressText = this.createElement('div', {
                    fontSize: '14px',
                    color: '#FF9800',
                    fontWeight: 'bold',
                    marginBottom: '8px',
                    '@media (max-width: 768px)': {
                        fontSize: '12px',
                        marginBottom: '6px'
                    }
                }, '📥 准备下载...');

                // PDF 生成进度
                this.pdfProgressText = this.createElement('div', {
                    fontSize: '14px',
                    color: '#673AB7',
                    fontWeight: 'bold',
                    '@media (max-width: 768px)': {
                        fontSize: '12px'
                    }
                }, '📄 等待下载完成...');

                this.progressInfoContainer.appendChild(this.chapterProgressText);
                this.progressInfoContainer.appendChild(this.currentChapterText);
                this.progressInfoContainer.appendChild(this.imageProgressText);
                this.progressInfoContainer.appendChild(this.pdfProgressText);

                this.container.appendChild(this.progressInfoContainer);
                this.progressContainer.style.display = 'none';

                this.startProgressSync();

            } else {
                // 恢复初始状态
                this.selectButton.disabled = false;
                this.selectButton.style.backgroundColor = '#4CAF50';
                this.selectButton.style.cursor = 'pointer';
                this.selectButtonText.textContent = '选择下载'; // ✅ 恢复文字

                this.scrollModeButton.style.display = 'flex'; // ✅ 改为 flex
                this.cancelSelectionButton.style.display = 'none';
                this.cancelButtonText.textContent = '返回';

                this.chapterListContainer.style.display = 'none';
                this.progressContainer.style.display = 'none';

                if (this.progressInfoContainer && this.progressInfoContainer.parentNode) {
                    this.progressInfoContainer.parentNode.removeChild(this.progressInfoContainer);
                }

                this.stopProgressSync();

                this.isSelectionMode = false;
                this.selectedChapters.clear();
                this.selectionStart = null;
                this.selectionEnd = null;
            }
        }
        // 添加实时进度同步方法
        startProgressSync() {
            if (this.progressSyncInterval) {
                clearInterval(this.progressSyncInterval);
            }

            let lastLogTime = 0;

            this.progressSyncInterval = setInterval(() => {
                try {
                    const currentChapter = GM_getValue('currentChapterName', '');
                    const currentImage = GM_getValue('currentImage', 0);
                    const totalImages = GM_getValue('totalImages', 0);
                    const downloadStatus = GM_getValue('downloadStatus', '');
                    const pdfGenerationComplete = GM_getValue('pdfGenerationComplete', false);
                    const currentPDFPage = GM_getValue('currentPDFPage', 0);
                    const totalPDFPages = GM_getValue('totalPDFPages', 0);

                    // ✅ 从按钮文本中提取章节进度(如果已设置)
                    const buttonText = this.selectButton.textContent;
                    let currentChapterNum = 0;
                    let totalChapters = 0;

                    // 尝试从按钮文本中解析进度
                    const match = buttonText.match(/第\s*(\d+)\s*\/\s*(\d+)\s*章/);
                    if (match) {
                        currentChapterNum = parseInt(match[1]);
                        totalChapters = parseInt(match[2]);
                    }

                    // ✅ 更新绿色按钮:只显示章节总进度
                    if (currentChapterNum > 0 && totalChapters > 0) {
                        this.selectButton.textContent = `正在下载第 ${currentChapterNum}/${totalChapters} 章`;
                    }

                    // ✅ 更新进度容器:显示详细信息
                    if (this.chapterProgressText && currentChapterNum > 0 && totalChapters > 0) {
                        this.chapterProgressText.textContent = `📖 章节进度: ${currentChapterNum}/${totalChapters}`;
                    }

                    if (this.currentChapterText) {
                        if (currentChapter) {
                            const displayName = currentChapter.length > 20
                                ? currentChapter.substring(0, 20) + '...'
                                : currentChapter;
                            this.currentChapterText.textContent = `📕 当前章节: ${displayName}`;
                        } else {
                            this.currentChapterText.textContent = `📕 当前章节: --`;
                        }
                    }

                    if (this.imageProgressText) {
                        if (totalImages > 0) {
                            this.imageProgressText.textContent = `📥 下载中: ${currentImage}/${totalImages}`;
                            this.imageProgressText.style.color = '#FF9800';
                        } else {
                            this.imageProgressText.textContent = `📥 准备下载...`;
                            this.imageProgressText.style.color = '#999';
                        }
                    }

                    if (this.pdfProgressText) {
                        if (currentImage >= totalImages && totalImages > 0) {
                            if (pdfGenerationComplete) {
                                this.pdfProgressText.textContent = `📄 PDF已生成完成!`;
                                this.pdfProgressText.style.color = '#4CAF50';
                            } else if (currentPDFPage > 0 && totalPDFPages > 0) {
                                this.pdfProgressText.textContent = `📄 正在生成PDF... ${currentPDFPage}/${totalPDFPages}`;
                                this.pdfProgressText.style.color = '#FF9800';
                            } else {
                                this.pdfProgressText.textContent = `📄 准备生成PDF...`;
                                this.pdfProgressText.style.color = '#FF9800';
                            }
                        } else if (totalImages > 0) {
                            this.pdfProgressText.textContent = `📄 等待下载完成...`;
                            this.pdfProgressText.style.color = '#999';
                        } else {
                            this.pdfProgressText.textContent = `📄 准备生成PDF...`;
                            this.pdfProgressText.style.color = '#999';
                        }
                    }

                    // 节流日志输出
                    const now = Date.now();
                    if (now - lastLogTime > 5000) {
                        if (currentChapterNum > 0) {
                            console.log(`📊 进度同步: 章节${currentChapterNum}/${totalChapters}, 图片${currentImage}/${totalImages}, PDF${currentPDFPage}/${totalPDFPages}`);
                        }
                        lastLogTime = now;
                    }
                } catch (error) {
                    console.error('进度同步出错:', error);
                }
            }, 300);
        }

        // 停止实时进度同步
        stopProgressSync() {
            if (this.progressSyncInterval) {
                clearInterval(this.progressSyncInterval);
                this.progressSyncInterval = null;
            }
        }

        // 修改 updateProgress 方法,使其更清晰地显示下载进度
        updateProgress(current, total, currentChapter = '', currentImage = 0, totalImages = 0) {
            // ✅ 只更新按钮的章节进度
            if (this.selectButton) {
                this.selectButton.textContent = `正在下载第 ${current}/${total} 章`;

            }
        }
    }

    class ComicDownloader {
        constructor() {
            try {
                console.log('开始创建ComicDownloader实例...');
                this.adapter = getSiteAdapter();
                this.isScrollMode = GM_getValue('isScrollMode', false); // ✅ 改名
                this.isDownloading = false;
                this.abortController = null;
                this.ui = null;
                this.initPromise = null;

                if (this.adapter.isChapterPage()) {
                    console.log('当前是章节页面');
                    this.initPromise = this.waitForImagesAndInit();
                } else if (this.adapter.isDirectoryPage()) {
                    console.log('当前是目录页面');
                    this.ui = new ChapterSelectorUI({
                        adapter: this.adapter,
                        onDownloadSelected: this.handleDownloadSelected.bind(this),
                        onToggleScrollMode: (isScroll) => { this.isScrollMode = isScroll; }, // ✅ 改名
                        onCancel: () => { },
                        onProgress: (current, total) => this.ui.updateProgress(current, total),
                        onLoading: (isLoading, total) => this.ui.setLoading(isLoading, total),
                        onComplete: () => { },
                        onError: (err) => this.ui.showError && this.ui.showError(err)
                    });
                    if (this.ui && this.ui.container) {
                        this.ui.container.style.display = 'flex';
                        this.ui.chapterListContainer.style.display = 'block';
                    }
                }
            } catch (error) {
                console.error('初始化失败:', error);
            }
        }

        // 添加等待UI初始化的方法
        async ensureUIReady() {
            if (this.initPromise) {
                await this.initPromise;
            }
            if (!this.ui) {
                throw new Error('UI初始化失败');
            }
        }

        // 在 ComicDownloader 类中,修改 waitForImagesAndInit 方法:

        async waitForImagesAndInit() {
            const maxAttempts = 12;  // ✅ 增加到12次,共6秒
            let attempts = 0;
            let imageElements = null;

            console.log('开始等待图片元素加载...');

            // 检查是否是批量下载模式
            const isBatchMode = GM_getValue('autoDownload', false);

            while (attempts < maxAttempts) {
                imageElements = this.adapter.getImageElements();
                const currentCount = imageElements.length;

                console.log(`[等待图片] 第 ${attempts + 1}/${maxAttempts} 次检查,找到 ${currentCount} 张图片`);

                // ✅ 批量模式下也要等待至少3次检查(1.5秒),确保不是占位图
                if (isBatchMode && currentCount > 0 && attempts >= 3) {
                    // ✅ 验证图片URL是否有效
                    const firstImg = imageElements[0];
                    const imgUrl = this.adapter.getImageUrl(firstImg);

                    if (imgUrl && !imgUrl.includes('loading') && !imgUrl.includes('placeholder')) {
                        console.log(`✓ 批量模式:找到 ${currentCount} 张有效图片,开始下载`);
                        break;
                    } else {
                        console.log(`⚠️ 检测到占位图,继续等待... URL: ${imgUrl}`);
                    }
                }

                // 单页模式:等待图片数量稳定且至少3次检查
                if (!isBatchMode && currentCount > 0 && attempts >= 3) {
                    console.log(`✓ 单页模式:图片加载稳定,共 ${currentCount} 张`);
                    break;
                }

                attempts++;
                await new Promise(resolve => setTimeout(resolve, 500));
            }

            this.totalPages = imageElements.length;
            this.chapterName = this.adapter.getChapterName();

            console.log('图片元素数量:', this.totalPages);
            console.log('章节名称:', this.chapterName);

            if (this.totalPages > 0) {
                console.log(`✓ 找到 ${this.totalPages} 页图片`);
                this.ui = new DownloaderUI(this.totalPages, this.handleDownload.bind(this), this.handleCancel.bind(this));
                if (this.isScrollMode) { // ✅ 改名
                    this.ui.isScrollMode = true; // ✅ 改名
                    this.ui.scrollModeButton.textContent = '切换普通模式'; // ✅ 改为 scrollModeButton
                    this.ui.scrollModeButton.style.backgroundColor = '#4CAF50';
                }
            } else {
                console.log('⚠️ 未找到图片元素');
                this.ui = new DownloaderUI(0, this.handleDownload.bind(this), this.handleCancel.bind(this));
            }
        }
        async handleDownload() {
            // ✅ 确保UI已初始化
            await this.ensureUIReady();

            if (this.isDownloading) {
                // ✅ 移除 alert,改为UI提示
                this.ui.infoText.textContent = '⚠️ 当前正在下载,请稍后再试';
                this.ui.infoText.style.display = 'block';
                this.ui.infoText.style.color = '#ff9800';

                setTimeout(() => {
                    this.ui.infoText.textContent = `本章节共 ${this.totalPages} 页`;
                    this.ui.infoText.style.color = '#4a5568';
                }, 2000);
                return;
            }

            try {
                this.isDownloading = true;
                this.abortController = new AbortController();
                this.ui.setLoading(true, true);
                this.isScrollMode = this.ui.isScrollMode;

                console.log('开始下载漫画...');
                await this.downloadComic();

                // ✅ PDF 已在 generatePDF 中真正保存完成后设置标志
                // 这里不需要再设置

                // 下载成功提示
                this.ui.infoText.textContent = '✓ 下载完成!';
                this.ui.infoText.style.display = 'block';
                this.ui.infoText.style.color = '#4CAF50';

                console.log('✓ 整个下载流程结束');

                setTimeout(() => {
                    this.ui.infoText.textContent = `本章节共 ${this.totalPages} 页`;
                    this.ui.infoText.style.color = '#4a5568';
                }, 3000);

            } catch (error) {
                if (error.name === 'AbortError' || error.message === 'AbortError') {
                    console.log('下载已被用户取消');
                    this.ui.infoText.textContent = '下载已取消';
                    this.ui.infoText.style.display = 'block';
                    this.ui.infoText.style.color = '#ff9800';

                    setTimeout(() => {
                        this.ui.infoText.textContent = `本章节共 ${this.totalPages} 页`;
                        this.ui.infoText.style.color = '#4a5568';
                    }, 2000);
                } else {
                    this.handleError(error, '下载失败');
                    this.ui.infoText.textContent = '❌ 下载失败,请重试';
                    this.ui.infoText.style.display = 'block';
                    this.ui.infoText.style.color = '#f44336';

                    setTimeout(() => {
                        this.ui.infoText.textContent = `本章节共 ${this.totalPages} 页`;
                        this.ui.infoText.style.color = '#4a5568';
                    }, 3000);
                }
            } finally {
                this.isDownloading = false;
                this.abortController = null;
                this.ui.setLoading(false, false);
            }
        }

        handleCancel() {
            if (!this.ui) {
                console.warn('UI还未初始化,无法取消下载');
                return;
            }

            if (this.abortController) {
                console.log('用户点击取消下载');
                this.abortController.abort();
                this.isDownloading = false;
                this.ui.setLoading(false, false);

                // 显示取消消息
                this.ui.infoText.textContent = '下载已取消';
                this.ui.infoText.style.display = 'block';
                this.ui.infoText.style.color = '#ff9800';

                setTimeout(() => {
                    this.ui.infoText.textContent = `本章节共 ${this.totalPages} 页`;
                    this.ui.infoText.style.color = '#4a5568';
                }, 2000);
            }
        }

        // 修改 downloadComic 方法
        // 1. 在 downloadComic 中添加状态设置
        async downloadComic() {
            console.log('开始下载漫画...');
            this.ui.infoText.textContent = '📥 下载中...';
            this.ui.infoText.style.display = 'block';
            this.ui.infoText.style.color = '#2196F3';

            let allImages = [];

            // ✅ 检查是否有分页
            if (this.adapter.hasMultiplePages()) {
                const pageUrls = this.adapter.getPageUrls();
                console.log(`检测到分页章节,共 ${pageUrls.length} 页`);

                // ✅ 收集所有分页的图片URL
                const allImageUrls = [];

                for (let i = 0; i < pageUrls.length; i++) {
                    const pageUrl = pageUrls[i];
                    const pageNum = i + 1;

                    console.log(`处理第 ${pageNum}/${pageUrls.length} 分页: ${pageUrl}`);

                    try {
                        let imageUrls;

                        if (pageUrl === window.location.href) {
                            // ✅ 当前页,直接从DOM获取图片URL
                            console.log(`第 ${pageNum} 页是当前页,直接获取图片URL`);
                            const imageElements = this.adapter.getImageElements();
                            imageUrls = Array.from(imageElements)
                                .map(img => this.adapter.getImageUrl(img))
                                .filter(url => url);
                        } else {
                            // ✅ 其他分页,后台加载获取图片URL
                            console.log(`第 ${pageNum} 页需要后台加载`);
                            imageUrls = await this.adapter.fetchImageUrlsFromPage(pageUrl);
                        }

                        console.log(`✓ 第 ${pageNum} 分页获取到 ${imageUrls.length} 张图片URL`);
                        allImageUrls.push(...imageUrls);

                    } catch (error) {
                        console.error(`第 ${pageNum} 分页处理失败:`, error);
                    }
                }

                console.log(`✓ 所有分页URL收集完成,共 ${allImageUrls.length} 张图片`);

                // ✅ 统一下载所有图片
                GM_setValue('totalImages', allImageUrls.length);
                allImages = await this.downloadImagesFromUrls(allImageUrls);

            } else {
                // 单页章节,正常下载
                allImages = await this.downloadImages(1, this.totalPages);
            }

            console.log('所有图片下载完成,开始生成PDF...');

            GM_setValue('downloadStatus', 'complete');
            console.log('✓ 已设置 downloadStatus 为 complete');

            this.ui.infoText.textContent = '📄 正在生成PDF...';
            this.ui.infoText.style.color = '#FF9800';

            await this.generatePDF(allImages);
        }
        async downloadImagesFromUrls(imageUrls) {
            console.log(`开始下载 ${imageUrls.length} 张图片`);
            const downloadResults = new Array(imageUrls.length);
            const downloadPromises = [];

            for (let i = 0; i < imageUrls.length; i++) {
                // 检查是否被取消
                if (this.abortController && this.abortController.signal.aborted) {
                    console.log('检测到取消信号,停止添加新的下载任务');
                    break;
                }

                const imgUrl = imageUrls[i];
                const pageNumber = i + 1;

                console.log(`添加第 ${pageNumber} 张图片下载任务: ${imgUrl}`);

                downloadPromises.push(
                    this.downloadImage(imgUrl)
                        .then(imgData => {
                            if (this.abortController && this.abortController.signal.aborted) {
                                console.log(`第 ${pageNumber} 张已下载但被标记为取消`);
                                return;
                            }

                            downloadResults[i] = imgData;
                            this.ui.infoText.textContent = `📥 下载中... ${pageNumber}/${imageUrls.length}`;
                            this.ui.infoText.style.color = '#2196F3';

                            GM_setValue('currentImage', pageNumber);
                            console.log(`第 ${pageNumber} 张下载完成`);
                        })
                        .catch(error => {
                            if (error.message === 'AbortError') {
                                console.log(`第 ${pageNumber} 张下载被取消`);
                            } else {
                                console.error(`第 ${pageNumber} 张下载失败:`, error);
                            }

                            downloadResults[i] = null;
                            GM_setValue('currentImage', pageNumber);
                        })
                );
            }

            console.log(`总共需要下载 ${downloadPromises.length} 张图片`);

            try {
                const results = await Promise.allSettled(downloadPromises);

                const cancelledCount = results.filter(r =>
                    r.status === 'rejected' && r.reason?.message === 'AbortError'
                ).length;

                if (cancelledCount > 0) {
                    console.log(`${cancelledCount} 个下载任务被取消`);
                    throw new Error('AbortError');
                }

                console.log('所有图片下载任务已完成');
                return downloadResults.filter(img => img !== null);
            } catch (error) {
                if (error.message === 'AbortError') {
                    console.log('下载被用户取消');
                    throw error;
                }
                throw error;
            }
        }

        async downloadImages(start, end) {
            console.log(`开始下载图片 ${start} 到 ${end}`);
            const imageElements = this.adapter.getImageElements();
            const downloadResults = new Array(end - start + 1);
            const downloadPromises = [];

            // 存储总图片数,供目录页面读取
            GM_setValue('totalImages', end - start + 1);

            for (let i = 0; i < imageElements.length; i++) {
                const pageNumber = i + 1;
                if (pageNumber >= start && pageNumber <= end) {
                    // ✅ 在添加promise前检查是否被取消
                    if (this.abortController && this.abortController.signal.aborted) {
                        console.log('检测到取消信号,停止添加新的下载任务');
                        break;
                    }

                    this.addDownloadPromise(imageElements[i], pageNumber, start, downloadResults, downloadPromises);
                }
            }

            console.log(`总共需要下载 ${downloadPromises.length} 张图片`);

            try {
                // ✅ 使用 Promise.allSettled 替代 Promise.all,这样不会因为一个失败而全部中止
                const results = await Promise.allSettled(downloadPromises);

                // 检查是否有被取消的请求
                const cancelledCount = results.filter(r =>
                    r.status === 'rejected' && r.reason?.message === 'AbortError'
                ).length;

                if (cancelledCount > 0) {
                    console.log(`${cancelledCount} 个下载任务被取消`);
                    throw new Error('AbortError');
                }

                console.log('所有图片下载任务已完成');
                return downloadResults;
            } catch (error) {
                if (error.message === 'AbortError') {
                    console.log('下载被用户取消');
                    throw error;
                }
                throw error;
            }
        }

        addDownloadPromise(element, pageNumber, start, downloadResults, downloadPromises) {
            const imgUrl = this.adapter.getImageUrl(element);
            if (imgUrl) {
                console.log(`添加第 ${pageNumber} 页图片下载任务: ${imgUrl}`);
                const arrayIndex = pageNumber - start;
                downloadPromises.push(
                    this.downloadImage(imgUrl)
                        .then(imgData => {
                            if (this.abortController && this.abortController.signal.aborted) {
                                console.log(`第 ${pageNumber} 页已下载但被标记为取消`);
                                return;
                            }

                            downloadResults[arrayIndex] = imgData;
                            // ✅ 修改:更新为统一的提示格式
                            this.ui.infoText.textContent = `📥 下载中... ${pageNumber}/${this.totalPages}`;
                            this.ui.infoText.style.color = '#2196F3';

                            GM_setValue('currentImage', pageNumber);
                            console.log(`第 ${pageNumber} 页下载完成`);
                        })
                        .catch(error => {
                            if (error.message === 'AbortError') {
                                console.log(`第 ${pageNumber} 页下载被取消`);
                            } else {
                                console.error(`第 ${pageNumber} 页下载失败:`, error);
                            }

                            downloadResults[arrayIndex] = null;
                            GM_setValue('currentImage', pageNumber);
                        })
                );
            } else {
                console.warn(`第 ${pageNumber} 页图片URL无效`);
            }
        }
        downloadImage(imgUrl) {
            return new Promise((resolve, reject) => {
                // 检查是否被取消
                if (this.abortController && this.abortController.signal.aborted) {
                    reject(new Error('AbortError'));
                    return;
                }

                console.log(`开始下载图片: ${imgUrl}`);

                GM_xmlhttpRequest({
                    method: 'GET',
                    url: imgUrl,
                    responseType: 'blob',
                    headers: {
                        'Referer': window.location.href,
                        'User-Agent': navigator.userAgent
                    },
                    onload: (response) => {
                        if (this.abortController && this.abortController.signal.aborted) {
                            reject(new Error('AbortError'));
                            return;
                        }

                        if (response.status === 200) {
                            this.handleImageResponse(response, resolve, reject);
                        } else {
                            console.error(`图片下载失败,状态码: ${response.status}`);
                            reject(new Error(`HTTP ${response.status}`));
                        }
                    },
                    onerror: (error) => {
                        console.error('图片下载出错:', error);
                        reject(error);
                    },
                    ontimeout: () => {
                        console.error('图片下载超时');
                        reject(new Error('下载超时'));
                    },
                    timeout: 30000 // 30秒超时
                });
            });
        }

        handleImageResponse(response, resolve, reject) {
            try {
                const blob = response.response;
                const reader = new FileReader();
                reader.onload = event => resolve(event.target.result);
                reader.onerror = error => reject(error);
                reader.readAsDataURL(blob);
            } catch (error) {
                reject(error);
            }
        }

        async generatePDF(images) {
            console.log('开始生成PDF...');

            this.ui.infoText.textContent = '📄 正在生成PDF...';
            this.ui.infoText.style.display = 'block';
            this.ui.infoText.style.color = '#FF9800';

            const pdf = new jspdf.jsPDF();
            const sizes = await this.getImageSizes(images);
            console.log('获取图片尺寸完成');

            if (this.isScrollMode) {
                console.log('使用滚动阅读模式生成PDF');
                await this.generateScrollModePDF(pdf, images, sizes);
            } else {
                console.log('使用普通模式生成PDF');
                for (let i = 0; i < images.length; i++) {
                    await this.addImageToPdf(pdf, images[i], i, sizes[i]);

                    const pdfProgress = i + 1;
                    const totalPDFPages = images.length;
                    GM_setValue('currentPDFPage', pdfProgress);
                    GM_setValue('totalPDFPages', totalPDFPages);

                    this.ui.infoText.textContent = `📄 正在生成PDF... ${pdfProgress}/${totalPDFPages}`;
                    console.log(`已添加第 ${i + 1} 页到PDF`);
                }
            }

            console.log('PDF生成完成,准备保存文件');

            this.ui.infoText.textContent = '💾 正在保存...';
            this.ui.infoText.style.color = '#673AB7';

            await this.savePDFWithWait(`${this.chapterName}.pdf`, pdf);

            console.log(`文件保存完成: ${this.chapterName}.pdf`);
            GM_setValue('pdfGenerationComplete', true);
            console.log('✓ PDF已真正保存,标志已设置');
        }
        // ✅ 新增:确保 PDF 下载完全完成的方法
        async savePDFWithWait(filename, pdf) {
            return new Promise((resolve) => {
                try {
                    const pdfBlob = pdf.output('blob');
                    const fileSize = pdfBlob.size;
                    const blobUrl = URL.createObjectURL(pdfBlob);

                    const link = document.createElement('a');
                    link.href = blobUrl;
                    link.download = filename;
                    link.style.display = 'none';
                    document.body.appendChild(link);

                    console.log(`准备下载PDF: ${filename}, 大小: ${(fileSize / 1024).toFixed(2)}KB`);

                    // 触发下载
                    link.click();
                    console.log('✓ 已触发浏览器下载');

                    // 计算等待时间(根据文件大小)
                    let waitTime = 500;
                    if (fileSize > 10 * 1024 * 1024) {
                        waitTime = 3000;
                    } else if (fileSize > 5 * 1024 * 1024) {
                        waitTime = 2000;
                    } else if (fileSize > 1 * 1024 * 1024) {
                        waitTime = 1500;
                    }

                    console.log(`PDF大小: ${(fileSize / 1024).toFixed(2)}KB,等待${waitTime}ms确保下载完成...`);

                    // ✅ 关键:等待足够长的时间,确保浏览器完全接收文件
                    setTimeout(() => {
                        // 清理资源
                        try {
                            document.body.removeChild(link);
                            URL.revokeObjectURL(blobUrl);
                            console.log('✓ 资源清理完成');
                        } catch (e) {
                            console.error('资源清理出错:', e);
                        }

                        console.log('✓ PDF保存流程完全结束,返回');
                        resolve();
                    }, waitTime);

                } catch (error) {
                    console.error('PDF保存失败:', error);
                    setTimeout(resolve, 1000);
                }
            });
        }

        async generateScrollModePDF(pdf, images, sizes) {
            console.log('开始生成滚动阅读模式PDF...');
            const A4_WIDTH = 210;  // A4纸宽度(毫米)
            const A4_HEIGHT = 297; // A4纸高度(毫米)

            // 过滤无效图片
            const validIndices = [];
            for (let i = 0; i < images.length; i++) {
                if (images[i] && images[i] !== 'null' && images[i] !== 'undefined') {
                    validIndices.push(i);
                } else {
                    console.warn(`第 ${i + 1} 张图片数据无效,已跳过`);
                }
            }

            if (validIndices.length === 0) {
                throw new Error('没有有效的图片可以生成PDF');
            }

            console.log(`有效图片数量: ${validIndices.length}/${images.length}`);

            // 删除默认的第一页
            pdf.deletePage(1);

            let currentPageImages = [];  // 当前页的图片
            let currentPageHeight = 0;   // 当前页的累计高度
            let totalPDFPages = 0;       // PDF总页数
            let processedImages = 0;     // 已处理的图片数

            for (let i = 0; i < validIndices.length; i++) {
                const idx = validIndices[i];

                // 计算图片按A4宽度缩放后的高度
                const scaleFactor = A4_WIDTH / sizes[idx].width;
                const scaledHeight = sizes[idx].height * scaleFactor;

                // 如果加上当前图片会超过A4高度,且当前页已有图片,则生成当前页
                if (currentPageHeight + scaledHeight > A4_HEIGHT && currentPageImages.length > 0) {
                    // 生成当前页
                    totalPDFPages++;
                    await this.addScrollPageToPDF(pdf, currentPageImages, A4_WIDTH, currentPageHeight);

                    // 更新进度
                    processedImages += currentPageImages.length;
                    GM_setValue('currentPDFPage', processedImages);
                    GM_setValue('totalPDFPages', validIndices.length);
                    this.ui.infoText.textContent = `📄 正在生成PDF... ${processedImages}/${validIndices.length}`;

                    console.log(`✓ 已生成第 ${totalPDFPages} 页PDF (包含 ${currentPageImages.length} 张图片)`);

                    // 重置当前页
                    currentPageImages = [];
                    currentPageHeight = 0;
                }

                // 添加图片到当前页
                currentPageImages.push({
                    data: images[idx],
                    width: A4_WIDTH,
                    height: scaledHeight,
                    index: idx
                });
                currentPageHeight += scaledHeight;
            }

            // 处理最后一页(如果还有剩余图片)
            if (currentPageImages.length > 0) {
                totalPDFPages++;
                await this.addScrollPageToPDF(pdf, currentPageImages, A4_WIDTH, currentPageHeight);

                processedImages += currentPageImages.length;
                GM_setValue('currentPDFPage', processedImages);
                GM_setValue('totalPDFPages', validIndices.length);

                console.log(`✓ 已生成第 ${totalPDFPages} 页PDF (包含 ${currentPageImages.length} 张图片)`);
            }

            console.log(`✓ 滚动阅读模式PDF生成完成,共 ${totalPDFPages} 页`);
        }

        async addScrollPageToPDF(pdf, pageImages, pageWidth, pageHeight) {
            // 添加新页面,高度为所有图片的累计高度
            pdf.addPage([pageWidth, pageHeight], 'portrait');

            let currentY = 0;

            // 依次添加图片到这一页
            for (let i = 0; i < pageImages.length; i++) {
                const imgData = pageImages[i];

                await new Promise((resolve, reject) => {
                    const img = new Image();
                    let isResolved = false;

                    const cleanup = () => {
                        img.onload = null;
                        img.onerror = null;
                        img.src = '';
                    };

                    img.onload = () => {
                        if (isResolved) return;
                        isResolved = true;

                        try {
                            pdf.addImage(
                                imgData.data,
                                'JPEG',
                                0,
                                currentY,
                                imgData.width,
                                imgData.height,
                                `image${imgData.index}`,
                                'FAST'
                            );

                            currentY += imgData.height;
                            cleanup();
                            setTimeout(resolve, 10);
                        } catch (error) {
                            console.error(`添加第 ${imgData.index + 1} 张图片失败:`, error);
                            cleanup();
                            reject(error);
                        }
                    };

                    img.onerror = () => {
                        if (isResolved) return;
                        isResolved = true;
                        console.warn(`第 ${imgData.index + 1} 张图片加载失败`);
                        cleanup();
                        resolve();
                    };

                    setTimeout(() => {
                        if (!isResolved) {
                            isResolved = true;
                            console.warn(`第 ${imgData.index + 1} 张图片加载超时`);
                            cleanup();
                            resolve();
                        }
                    }, 5000);

                    img.src = imgData.data;
                });
            }
        }
        async getImageSizes(images) {
            return Promise.all(images.map(imgData => {
                return new Promise(resolve => {
                    const img = new Image();
                    img.src = imgData;
                    img.onload = () => resolve({ width: img.width, height: img.height });
                });
            }));
        }

        async addImageToPdf(pdf, imgData, index, size) {
            return new Promise(resolve => {
                const img = new Image();
                img.src = imgData;
                img.onload = () => {
                    if (index > 0) pdf.addPage();

                    const A4_width = 210;
                    const A4_height = 297;
                    const scaleFactor = A4_width / size.width;
                    let finalWidth = A4_width;
                    let finalHeight = size.height * scaleFactor;

                    if (finalHeight > A4_height) {
                        finalHeight = A4_height;
                        finalWidth = size.width * (A4_height / size.height);
                    }

                    pdf.internal.pageSize.width = finalWidth;
                    pdf.internal.pageSize.height = finalHeight;
                    pdf.addImage(imgData, 'JPEG', 0, 0, finalWidth, finalHeight);
                    console.log(`已添加第 ${index + 1} 页到PDF,尺寸: ${finalWidth}x${finalHeight}`);
                    resolve();
                };
            });
        }

        handleError(error, message = '下载失败') {
            console.error(message, error);
        }

        // 添加处理选中章节下载的方法
        async handleDownloadSelected() {
            console.log('开始处理选中章节下载...');
            const selectedChapters = this.ui.selectedChapters;
            if (selectedChapters.size === 0) {
                console.log('未选择任何章节');
                this.ui.selectButton.textContent = '⚠️ 请选择至少一个章节';
                this.ui.selectButton.style.backgroundColor = '#ff9800';
                setTimeout(() => {
                    this.ui.selectButton.textContent = '选择章节下载';
                    this.ui.selectButton.style.backgroundColor = '#4CAF50';
                }, 2000);
                return;
            }

            try {
                const chapterLinks = await this.adapter.getChapterLinks();
                const selectedChapterUrls = Array.from(selectedChapters).map(index => chapterLinks[index].url);
                const chapterCount = selectedChapterUrls.length;

                console.log(`准备批量下载 ${chapterCount} 个章节`);

                // 清除取消标志
                GM_setValue('cancelBatchDownload', false);

                this.ui.setLoading(true, chapterCount);
                this.isScrollMode = this.ui.isScrollMode;

                const batchSessionId = Date.now().toString();
                GM_setValue('isScrollMode', this.isScrollMode);

                // ✅ 设置批量下载标志 - 用于禁用图片加载
                GM_setValue('isBatchDownload', true);

                console.log(`创建批量下载会话: ${batchSessionId}`);


                // 存储下载失败的章节
                const failedChapters = [];
                const cancelledChapters = [];
                let currentTab = null; // ✅ 当前打开的标签页

                for (let i = 0; i < selectedChapterUrls.length; i++) {
                    // 检查是否被取消
                    if (GM_getValue('cancelBatchDownload', false)) {
                        console.log('检测到取消标志,停止批量下载');
                        cancelledChapters.push(...selectedChapterUrls.slice(i).map((url, idx) => {
                            return chapterLinks[Array.from(selectedChapters)[i + idx]].name;
                        }));

                        // 关闭当前标签页
                        if (currentTab) {
                            console.log('关闭当前标签页...');
                            try {
                                currentTab.close();
                                console.log('标签页已关闭');
                            } catch (e) {
                                console.log('标签页关闭失败:', e);
                            }
                            currentTab = null;
                        }
                        break;
                    }

                    const url = selectedChapterUrls[i];
                    const chapterName = chapterLinks[Array.from(selectedChapters)[i]].name;

                    console.log(`准备下载第 ${i + 1}/${chapterCount} 个章节: ${chapterName}`);

                    // 更新进度 - 显示正在下载哪个章节
                    this.ui.updateProgress(i, chapterCount, chapterName, 0, 0);

                    try {
                        // ✅ 生成唯一的会话ID,避免冲突
                        const sessionId = `${batchSessionId}_${i}`;

                        GM_setValue('autoDownload', true);
                        GM_setValue('sessionId', sessionId);
                        GM_setValue('downloadStatus', 'pending');
                        GM_setValue('currentChapterName', chapterName);
                        GM_setValue('currentImage', 0);
                        GM_setValue('totalImages', 0);
                        // ✅ 新增:重置 PDF 进度值
                        GM_setValue('currentPDFPage', 0);
                        GM_setValue('totalPDFPages', 0);
                        GM_setValue('pdfGenerationComplete', false);

                        console.log(`设置下载状态为: pending,会话ID: ${sessionId}`);
                        // ✅ 先关闭上一个标签页,再打开新的
                        if (currentTab) {
                            try {
                                currentTab.close();
                                console.log('已关闭上个标签页');
                            } catch (e) {
                                console.log('上个标签页关闭失败:', e);
                            }
                            // 等待标签页完全关闭
                            await new Promise(resolve => setTimeout(resolve, 500));
                        }

                        currentTab = GM_openInTab(url, {
                            active: false,
                            insert: true,
                            setParent: true
                        });

                        console.log(`已打开标签页: ${url}`);

                        // 等待下载完成,同时监听图片下载进度
                        await new Promise((resolve, reject) => {
                            const maxWaitTime = 120000; // 图片下载阶段最多等待2分钟
                            const maxPdfWaitTimeWithoutProgress = 60000; // ✅ 改为:PDF生成无进度时最多等待1分钟
                            const startTime = Date.now();
                            let resolved = false;
                            let hasStartedDownloading = false;
                            let downloadCompleteTime = null;
                            let pdfGenerationStartTime = null;
                            let pdfGenerationCompleteTime = null;
                            let lastPDFPage = 0; // ✅ 新增:记录上次的PDF页数
                            let lastPDFProgressTime = Date.now(); // ✅ 新增:记录上次PDF进度更新时间

                            const checkStatus = () => {
                                if (resolved) return;

                                const currentTimeMs = Date.now();
                                const elapsedTime = currentTimeMs - startTime;

                                // 检查是否被取消
                                if (GM_getValue('cancelBatchDownload', false)) {
                                    resolved = true;
                                    GM_setValue('downloadStatus', '');
                                    GM_setValue('currentImage', 0);
                                    GM_setValue('totalImages', 0);
                                    GM_setValue('currentPDFPage', 0);
                                    GM_setValue('totalPDFPages', 0);
                                    console.log('下载被用户取消');
                                    reject(new Error('用户取消下载'));
                                    return;
                                }

                                const status = GM_getValue('downloadStatus', '');
                                const currentImage = GM_getValue('currentImage', 0);
                                const totalImages = GM_getValue('totalImages', 0);
                                const currentPDFPage = GM_getValue('currentPDFPage', 0);
                                const totalPDFPages = GM_getValue('totalPDFPages', 0);
                                const pdfGenerationComplete = GM_getValue('pdfGenerationComplete', false);

                                if (totalImages > 0) {
                                    hasStartedDownloading = true;
                                }

                                const lastImageCount = GM_getValue('lastImageCount', 0);
                                if (totalImages > 0 && currentImage !== lastImageCount) {
                                    this.ui.updateProgress(i + 1, chapterCount, chapterName, currentImage, totalImages);
                                    GM_setValue('lastImageCount', currentImage);
                                }

                                const isDownloadComplete =
                                    (status === 'complete' && hasStartedDownloading) ||
                                    (hasStartedDownloading && currentImage >= totalImages && totalImages > 0);

                                if (isDownloadComplete && !downloadCompleteTime) {
                                    downloadCompleteTime = currentTimeMs;
                                    console.log(`✓ 第 ${i + 1} 个章节图片下载完成,等待PDF生成...`);
                                }

                                if (downloadCompleteTime && !pdfGenerationStartTime && currentPDFPage > 0) {
                                    pdfGenerationStartTime = currentTimeMs;
                                    lastPDFPage = currentPDFPage;
                                    lastPDFProgressTime = currentTimeMs;
                                    console.log(`✓ 检测到 PDF 生成开始`);
                                }

                                // ✅ 新增:检测PDF生成进度
                                if (pdfGenerationStartTime && currentPDFPage > lastPDFPage) {
                                    lastPDFPage = currentPDFPage;
                                    lastPDFProgressTime = currentTimeMs;
                                    console.log(`📄 PDF生成进度更新: ${currentPDFPage}/${totalPDFPages}`);
                                }

                                if (pdfGenerationStartTime && !pdfGenerationCompleteTime && pdfGenerationComplete) {
                                    pdfGenerationCompleteTime = currentTimeMs;
                                    console.log(`✓ 检测到 PDF 生成完成`);
                                }

                                if (pdfGenerationCompleteTime) {
                                    const waitAfterComplete = currentTimeMs - pdfGenerationCompleteTime;

                                    if (waitAfterComplete >= 5000) {
                                        resolved = true;
                                        GM_setValue('downloadStatus', '');
                                        GM_setValue('currentImage', 0);
                                        GM_setValue('totalImages', 0);
                                        GM_setValue('currentPDFPage', 0);
                                        GM_setValue('totalPDFPages', 0);
                                        GM_setValue('pdfGenerationComplete', false);
                                        GM_setValue('lastImageCount', 0);
                                        console.log(`✓ 第 ${i + 1} 个章节完成: ${chapterName}`);
                                        resolve();
                                        return;
                                    }
                                } else if (pdfGenerationStartTime) {
                                    const timeSinceLastProgress = currentTimeMs - lastPDFProgressTime;

                                    // ✅ 改进:如果PDF正在生成且有进度,就继续等待
                                    if (currentPDFPage > 0 && currentPDFPage < totalPDFPages) {
                                        // PDF正在生成中,检查是否长时间无进度
                                        if (timeSinceLastProgress > maxPdfWaitTimeWithoutProgress) {
                                            console.warn(`⚠️ PDF生成超过${maxPdfWaitTimeWithoutProgress / 1000}秒无进度,当前${currentPDFPage}/${totalPDFPages}`);
                                            resolved = true;
                                            GM_setValue('downloadStatus', '');
                                            GM_setValue('currentImage', 0);
                                            GM_setValue('totalImages', 0);
                                            GM_setValue('currentPDFPage', 0);
                                            GM_setValue('totalPDFPages', 0);
                                            GM_setValue('pdfGenerationComplete', false);
                                            GM_setValue('lastImageCount', 0);
                                            console.log(`⚠️ 第 ${i + 1} 个章节PDF生成超时: ${chapterName}`);
                                            failedChapters.push(chapterName);
                                            reject(new Error('PDF生成超时'));
                                            return;
                                        }
                                    } else if (currentPDFPage >= totalPDFPages && totalPDFPages > 0) {
                                        // ✅ PDF已经生成完所有页,等待保存完成标志
                                        if (timeSinceLastProgress > 30000) { // 等待30秒保存
                                            resolved = true;
                                            GM_setValue('downloadStatus', '');
                                            GM_setValue('currentImage', 0);
                                            GM_setValue('totalImages', 0);
                                            GM_setValue('currentPDFPage', 0);
                                            GM_setValue('totalPDFPages', 0);
                                            GM_setValue('pdfGenerationComplete', false);
                                            GM_setValue('lastImageCount', 0);
                                            console.log(`✓ 第 ${i + 1} 个章节完成(PDF已生成): ${chapterName}`);
                                            resolve();
                                            return;
                                        }
                                    }
                                } else if (downloadCompleteTime) {
                                    const waitForPdfStart = currentTimeMs - downloadCompleteTime;

                                    // ✅ 增加到60秒,并输出更详细的日志
                                    if (waitForPdfStart > 60000) {
                                        console.warn(`⚠️ 图片下载完成已${Math.floor(waitForPdfStart / 1000)}秒,但PDF仍未开始生成`);
                                        console.warn(`   当前状态: downloadStatus=${GM_getValue('downloadStatus')}`);
                                        console.warn(`   currentPDFPage=${GM_getValue('currentPDFPage')}, totalPDFPages=${GM_getValue('totalPDFPages')}`);

                                        resolved = true;
                                        GM_setValue('downloadStatus', '');
                                        GM_setValue('currentImage', 0);
                                        GM_setValue('totalImages', 0);
                                        GM_setValue('currentPDFPage', 0);
                                        GM_setValue('totalPDFPages', 0);
                                        GM_setValue('lastImageCount', 0);
                                        console.warn(`✗ 第 ${i + 1} 个章节超时: ${chapterName}`);
                                        failedChapters.push(chapterName);
                                        reject(new Error('PDF生成未开始'));
                                        return;
                                    }

                                    // ✅ 添加:每10秒输出一次等待日志
                                    if (Math.floor(waitForPdfStart / 10000) > Math.floor((waitForPdfStart - 500) / 10000)) {
                                        console.log(`⏳ 等待PDF生成开始... 已等待${Math.floor(waitForPdfStart / 1000)}秒`);
                                    }
                                }
                                else if (elapsedTime > maxWaitTime) {
                                    resolved = true;
                                    GM_setValue('downloadStatus', '');
                                    GM_setValue('currentImage', 0);
                                    GM_setValue('totalImages', 0);
                                    GM_setValue('currentPDFPage', 0);
                                    GM_setValue('totalPDFPages', 0);
                                    GM_setValue('lastImageCount', 0);
                                    console.warn(`✗ 第 ${i + 1} 个章节下载超时: ${chapterName}`);
                                    failedChapters.push(chapterName);
                                    reject(new Error('下载超时'));
                                    return;
                                }
                            };

                            const checkInterval = setInterval(() => {
                                checkStatus();
                                if (resolved) {
                                    clearInterval(checkInterval);
                                }
                            }, 500);

                            // ✅ 修改:移除固定的总超时时间,依赖各阶段的超时检测
                            // 仅保留一个非常长的保底超时(10分钟)
                            const emergencyTimeout = setTimeout(() => {
                                if (!resolved) {
                                    resolved = true;
                                    clearInterval(checkInterval);
                                    console.warn(`✗ 第 ${i + 1} 个章节紧急超时(10分钟)`);
                                    reject(new Error('紧急超时'));
                                }
                            }, 600000); // 10分钟

                            // 清理函数
                            const cleanup = () => {
                                clearInterval(checkInterval);
                                clearTimeout(emergencyTimeout);
                            };

                            // 在resolve/reject时清理定时器
                            const originalResolve = resolve;
                            const originalReject = reject;
                            resolve = (...args) => {
                                cleanup();
                                originalResolve(...args);
                            };
                            reject = (...args) => {
                                cleanup();
                                originalReject(...args);
                            };
                        });


                        // 更新为完成状态
                        this.ui.updateProgress(i + 1, chapterCount);

                        // 章节间延迟 - 在下载完成后
                        if (i < chapterCount - 1) {
                            console.log(`等待2秒后下载下一个章节...`); // ✅ 改为2秒(原来3秒)
                            await new Promise(resolve => setTimeout(resolve, 2000));
                        }

                        // 失败后延迟 - 在 catch 块中
                    } catch (error) {
                        if (error.message === '用户取消下载') {
                            console.log('用户取消下载,跳出循环');
                            cancelledChapters.push(chapterName);
                            break;
                        }
                        console.error(`✗ 第 ${i + 1} 个章节下载失败: ${chapterName}`, error);
                        failedChapters.push(chapterName);

                        await new Promise(resolve => setTimeout(resolve, 500)); // ✅ 改为0.5秒(原来1秒)
                    }
                }

                // 关闭最后打开的标签页
                if (currentTab) {
                    console.log('关闭最后一个标签页...');
                    try {
                        currentTab.close();
                        console.log('最后一个标签页已关闭');
                    } catch (e) {
                        console.log('最后一个标签页关闭失败:', e);
                    }
                }

                // 清除批量下载标志
                GM_setValue('isBatchDownload', false);
                GM_setValue('autoDownload', false);
                GM_setValue('sessionId', '');
                GM_setValue('currentChapterName', '');
                GM_setValue('cancelBatchDownload', false);
                GM_setValue('downloadStatus', '');
                GM_setValue('currentPDFPage', 0);
                GM_setValue('totalPDFPages', 0);


                this.ui.setLoading(false);
                console.log('批量下载流程结束');

                // 显示完成统计
                const successCount = chapterCount - failedChapters.length - cancelledChapters.length;

                if (cancelledChapters.length > 0) {
                    // ✅ 移除 alert,使用按钮显示
                    this.ui.selectButton.textContent = `⏸️ 已取消 (完成${successCount}/${chapterCount})`;
                    this.ui.selectButton.style.backgroundColor = '#ff9800';

                    console.log(`批量下载已取消\n✓ 已完成: ${successCount}个\n✗ 失败: ${failedChapters.length}个\n⊗ 已取消: ${cancelledChapters.length}个`);

                    // ✅ 如果有失败的章节,在控制台输出详情
                    if (failedChapters.length > 0) {
                        console.warn('失败章节:', failedChapters.join(', '));
                    }
                } else if (failedChapters.length === 0) {
                    this.ui.selectButton.textContent = `🎉 全部完成! (${chapterCount}个章节)`;
                    this.ui.selectButton.style.backgroundColor = '#4CAF50';
                    console.log(`✓ 批量下载全部完成! 共${chapterCount}个章节`);
                } else {
                    // ✅ 移除 alert,使用按钮显示
                    this.ui.selectButton.textContent = `⚠️ 部分完成 (${successCount}/${chapterCount})`;
                    this.ui.selectButton.style.backgroundColor = '#ff9800';

                    console.warn(`下载完成! 成功: ${successCount}个, 失败: ${failedChapters.length}个`);
                    console.warn('失败章节:', failedChapters.join(', '));
                }

                this.ui.selectButton.disabled = true;

                setTimeout(() => {
                    this.ui.selectButton.textContent = '选择章节下载';
                    this.ui.selectButton.style.backgroundColor = '#4CAF50';
                    this.ui.selectButton.disabled = false;
                }, 5000);

            } catch (error) {
                console.error('批量下载失败:', error);
                GM_setValue('autoDownload', false);
                GM_setValue('sessionId', '');
                GM_setValue('cancelBatchDownload', false);
                GM_setValue('downloadStatus', '');
                GM_setValue('isBatchDownload', false); // ✅ 添加这行


                this.ui.setLoading(false);
                this.ui.selectButton.textContent = '❌ 下载失败,请重试';
                this.ui.selectButton.style.backgroundColor = '#f44336';
                setTimeout(() => {
                    this.ui.selectButton.textContent = '选择章节下载';
                    this.ui.selectButton.style.backgroundColor = '#4CAF50';
                }, 3000);
            }
        }
    }
    // 在 initialize 函数之前添加样式
    function addScrollbarStyles() {
        if (document.getElementById('comic-downloader-scrollbar-styles')) {
            return; // 已添加,避免重复
        }

        const styleSheet = document.createElement('style');
        styleSheet.id = 'comic-downloader-scrollbar-styles';
        styleSheet.textContent = `
        /* 自定义滚动条样式 */
        .comic-downloader-chapter-list::-webkit-scrollbar {
            width: 8px;
        }
        
        .comic-downloader-chapter-list::-webkit-scrollbar-track {
            background: rgba(0,0,0,0.05);
            border-radius: 4px;
        }
        
        .comic-downloader-chapter-list::-webkit-scrollbar-thumb {
            background: rgba(0,0,0,0.2);
            border-radius: 4px;
            transition: background 0.2s;
        }
        
        .comic-downloader-chapter-list::-webkit-scrollbar-thumb:hover {
            background: rgba(0,0,0,0.4);
        }
        
        /* Firefox */
        .comic-downloader-chapter-list {
            scrollbar-width: thin;
            scrollbar-color: rgba(0,0,0,0.2) rgba(0,0,0,0.05);
        }
    `;
        document.head.appendChild(styleSheet);
    }
    // 6. 初始化
    function initialize() {
        console.log('开始初始化下载器...');
        addScrollbarStyles();

        // ✅ 检查是否是批量下载模式
        const isBatchDownload = GM_getValue('isBatchDownload', false);
        const autoDownload = GM_getValue('autoDownload', false);

        if (isBatchDownload && autoDownload) {
            console.log('🚫 批量下载模式:禁用图片加载');
            disableImageLoading();
        }

        try {
            window.comicDownloader = new ComicDownloader();

            const sessionId = GM_getValue('sessionId', '');
            const currentTime = Date.now();

            console.log('自动下载标志:', autoDownload);
            console.log('会话ID:', sessionId);

            if (autoDownload &&
                sessionId &&
                window.comicDownloader.adapter.isChapterPage() &&
                (currentTime - parseInt(sessionId.split('_')[0])) < 300000) {

                console.log('检测到批量下载流程,准备自动下载');

                window.comicDownloader.isScrollMode = GM_getValue('isScrollMode', false);

                window.comicDownloader.ensureUIReady().then(() => {
                    // ✅ 增加延迟时间,给页面更多时间初始化
                    setTimeout(async () => {
                        try {
                            console.log('等待页面初始化完成,开始自动下载...');
                            await window.comicDownloader.handleDownload();
                            console.log('自动下载完成');
                            GM_setValue('downloadStatus', 'complete');

                            // 下载完成后恢复图片加载(为下一个章节做准备)
                            if (isBatchDownload) {
                                enableImageLoading();
                            }
                        } catch (error) {
                            console.error('自动下载失败:', error);
                            GM_setValue('downloadStatus', 'complete');

                            if (isBatchDownload) {
                                enableImageLoading();
                            }
                        }
                    }, 2000); // ✅ 改为2秒,给页面足够的初始化时间
                }).catch(error => {
                    console.error('UI初始化失败:', error);
                    GM_setValue('downloadStatus', 'failed');
                    GM_setValue('autoDownload', false);

                    if (isBatchDownload) {
                        enableImageLoading();
                    }
                });

            } else {
                if (autoDownload) {
                    console.log('清除自动下载标志');
                    GM_setValue('autoDownload', false);
                    GM_setValue('sessionId', '');
                    GM_setValue('isBatchDownload', false);
                }
            }

            if (window.comicDownloader.ui) {
                console.log('UI已初始化');
                if (window.comicDownloader.adapter.isDirectoryPage()) {
                    console.log('当前是目录页面');
                    window.comicDownloader.ui.chapterListContainer.style.display = 'block';
                }
            } else {
                console.log('等待章节页面UI初始化...');
            }
        } catch (error) {
            console.error('初始化失败:', error);
        }
    }

    // 确保在页面完全加载后再初始化
    if (document.readyState === 'complete') {
        console.log('页面已加载完成,立即初始化');
        initialize();
    } else {
        console.log('等待页面加载完成...');
        window.addEventListener('load', () => {
            console.log('页面加载完成,开始初始化');
            initialize();
        });
    }
})();