/** * @Author: xhg * @Date: 2025-02-04 13:45:10 * @Last Modified by: xhg * @Last Modified time: 2025-02-04 16:24:15 */ 'use strict'; // ==UserScript== // @name 📚【小说下载神器📥,适配PC+移动端 // @namespace http://tampermonkey.net/ // @version 0.1 // @description 自动下载并合并小说章节内容 // @author xhg // @match https://www.deqixs.com/xiaoshuo/* // @grant GM_xmlhttpRequest // @grant GM_download // @connect deqixs.com // ==/UserScript== (function() { 'use strict'; // 新增:检查当前是否在TXT目录页 if (!location.href.includes('txt.html')) { // 查找TXT下载链接 const txtLink = document.querySelector('h2.op.bb a[href*="txt.html"]'); if (txtLink) { // 自动跳转到TXT目录页 window.location.href = new URL(txtLink.href, location.href).href; return; // 终止当前脚本执行,等待跳转后重新执行 } else { console.log('未找到TXT下载链接!'); return; } } // 创建下载按钮(调整尺寸) const btn = document.createElement('button'); btn.innerHTML = '📚 合并下载'; btn.style.padding = '8px 16px'; // 缩小按钮尺寸 btn.style.fontSize = '14px'; // 调整字体大小 btn.style.position = 'fixed'; btn.style.right = '20px'; btn.style.top = '20px'; btn.style.zIndex = 9999; btn.style.backgroundColor = '#4CAF50'; btn.style.color = 'white'; btn.style.border = 'none'; btn.style.borderRadius = '5px'; btn.style.cursor = 'pointer'; btn.style.boxShadow = '0 2px 5px rgba(0,0,0,0.2)'; btn.style.display = 'flex'; btn.style.alignItems = 'center'; btn.style.gap = '8px'; btn.addEventListener('mouseover', () => btn.style.backgroundColor = '#45a049'); btn.addEventListener('mouseout', () => btn.style.backgroundColor = '#4CAF50'); document.body.appendChild(btn); btn.addEventListener('click', async () => { console.log('下载按钮被点击'); try { // 增强标题处理(处理多种字数格式) const rawTitle = document.querySelector('h1').textContent.trim(); const novelTitle = rawTitle .replace(/^[\d.]+万字\s*/, '') // 支持小数格式(如"123.4万字") .replace(/\s+/g, '_') .replace(/[\\/:*?"<>|]/g, ''); // 去除非法文件名字符 console.log('处理后的标题:', novelTitle); // 获取所有章节链接 const links = Array.from(document.querySelectorAll('#list ul li a')) .map(a => ({ url: new URL(a.href, location.href).href, title: a.textContent })); console.log('找到章节链接:', links.length, '个'); // 调试信息 // 创建进度容器(优化样式) const progressContainer = document.createElement('div'); progressContainer.style.position = 'fixed'; progressContainer.style.right = '20px'; progressContainer.style.top = '60px'; progressContainer.style.background = 'rgba(255,255,255,0.9)'; progressContainer.style.padding = '10px'; progressContainer.style.borderRadius = '8px'; progressContainer.style.boxShadow = '0 2px 8px rgba(0,0,0,0.2)'; // 进度条元素 const progressBar = document.createElement('div'); progressBar.style.width = '200px'; progressBar.style.height = '8px'; progressBar.style.background = '#eee'; progressBar.style.borderRadius = '4px'; progressBar.style.marginBottom = '8px'; // 进度填充 const progressFill = document.createElement('div'); progressFill.style.width = '0%'; progressFill.style.height = '100%'; progressFill.style.background = '#4CAF50'; progressFill.style.borderRadius = '4px'; progressFill.style.transition = 'width 0.3s ease'; progressBar.appendChild(progressFill); // 进度文本 const progressText = document.createElement('div'); progressText.style.textAlign = 'center'; progressText.style.color = '#666'; progressText.style.fontSize = '12px'; progressContainer.appendChild(progressBar); progressContainer.appendChild(progressText); document.body.appendChild(progressContainer); let totalChapters = 0; let downloadedChapters = 0; // 预解析所有章节数 links.forEach(link => { const match = link.title.match(/(\d+)-(\d+)章/); if (match) { const start = parseInt(match[1]); const end = parseInt(match[2]); if (start > end) { // 处理异常数据 console.warn('异常章节范围:', link.title); totalChapters += 1; } else { totalChapters += end - start + 1; } } else { totalChapters += 1; } }); // 重置进度 let realDownloaded = 0; // 真实下载计数 let simulatedProgress = 0; // 模拟进度计数 progressText.textContent = `初始化中... (总${totalChapters}章)`; let fullContent = ''; for (const [index, link] of links.entries()) { try { // 解析当前链接包含的章节数 const match = link.title.match(/(\d+)-(\d+)章/); const chaptersInLink = match ? parseInt(match[2]) - parseInt(match[1]) + 1 : 1; // 新的更新方法 const updateProgress = () => { const totalProgress = realDownloaded + simulatedProgress; const percent = Math.min((totalProgress / totalChapters * 100), 100).toFixed(1); progressFill.style.width = `${percent}%`; progressText.textContent = `下载进度:${percent}% (${totalProgress}/${totalChapters}章)`; }; // 修改后的模拟进度 const simulateProgress = (chapters) => { let current = 0; const interval = setInterval(() => { if (current < chapters && realDownloaded === 0) { current++; simulatedProgress = current; updateProgress(); } else { clearInterval(interval); simulatedProgress = 0; // 重置模拟进度 updateProgress(); } }, 150); }; simulateProgress(chaptersInLink); // 开始模拟进度 // 修改后的下载处理 const content = await fetchText(link.url); realDownloaded += chaptersInLink; // 记录真实进度 updateProgress(); fullContent += `\n\n${link.title}\n\n${content}`; } catch (err) { console.error('下载失败:', err); alert(`下载失败: ${link.title}`); return; } } // 下载完成提示 progressContainer.innerHTML = `
✅ 下载完成! (${links.length}章已合并)
`; // 3秒后自动消失 setTimeout(() => { document.body.removeChild(progressContainer); }, 3000); // 生成合并文件(修改文件名) const blob = new Blob([fullContent], { type: 'text/plain' }); const url = URL.createObjectURL(blob); GM_download({ url: url, name: `${novelTitle}_精校版.txt`, saveAs: true }); } catch (err) { console.error('初始化错误:', err); // 更详细的错误日志 alert('初始化失败,请检查控制台日志'); } }); // 封装异步请求 function fetchText(url) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: url, responseType: 'text', onload: (res) => { if (res.status === 200) { resolve(res.responseText); } else { reject(new Error(`HTTP ${res.status}`)); } }, onerror: reject }); }); } })();