// ==UserScript== // @name KDown - 百度网盘加速下载 // @namespace https://kdown.moiu.cn // @version 1.0.1 // @description 百度网盘免费加速下载工具,支持批量下载、多种下载方式、积分系统、在线解析 // @author MoTeam、XiaoMo、CheckOut // @match *://pan.baidu.com/disk/home* // @match *://pan.baidu.com/disk/main* // @match *://pan.baidu.com/s/* // @match *://yun.baidu.com/disk/home* // @match *://yun.baidu.com/disk/main* // @match *://yun.baidu.com/s/* // @icon https://nd-static.bdstatic.com/m-static/v20-main/favicon-main.ico // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_addStyle // @grant GM_registerMenuCommand // @grant GM_cookie // @grant GM_notification // @grant GM_getResourceText // @grant unsafeWindow // @connect moiu.cn // @connect assets.moiu.cn // @connect jiexi.moiu.cn // @connect api.moiu.cn // @connect shop.moiu.cn // @connect baidu.com // @connect baidupcs.com // @connect * // @resource KDOWN_STYLE https://assets.moiu.cn/css/kdown/style.css // @license MIT // ==/UserScript== // GM_cookie 包装器 const GM_cookie_wrapper = { list: (details) => { return new Promise((resolve, reject) => { if (typeof GM_cookie !== 'undefined') { GM_cookie.list(details, (cookies) => { resolve(cookies); }, (error) => { reject(error); }); } else if (typeof GM !== 'undefined' && GM.cookie) { GM.cookie.list(details).then(resolve).catch(reject); } else { reject(new Error('GM_cookie not available')); } }); }, set: (details) => { return new Promise((resolve, reject) => { if (typeof GM_cookie !== 'undefined') { GM_cookie.set(details, () => { resolve(); }, (error) => { reject(error); }); } else if (typeof GM !== 'undefined' && GM.cookie) { GM.cookie.set(details).then(resolve).catch(reject); } else { reject(new Error('GM_cookie not available')); } }); }, delete: (details) => { return new Promise((resolve, reject) => { if (typeof GM_cookie !== 'undefined') { GM_cookie.delete(details, () => { resolve(); }, (error) => { reject(error); }); } else if (typeof GM !== 'undefined' && GM.cookie) { GM.cookie.delete(details).then(resolve).catch(reject); } else { reject(new Error('GM_cookie not available')); } }); } }; // locals 包装器 const locals_wrapper = { get: (key) => { try { if (typeof unsafeWindow.locals !== 'undefined' && typeof unsafeWindow.locals.get === 'function') { return unsafeWindow.locals.get(key); } // 尝试从页面中提取 const scriptContent = document.body.innerHTML; const pattern = new RegExp(`"${key}":"([^"]+)"`); const match = scriptContent.match(pattern); if (match && match[1]) { return match[1]; } return null; } catch (e) { Logger.error('获取locals失败', e); return null; } }, set: (key, value) => { try { if (typeof unsafeWindow.locals !== 'undefined' && typeof unsafeWindow.locals.set === 'function') { unsafeWindow.locals.set(key, value); } } catch (e) { Logger.error('设置locals失败', e); } } }; // 日志工具类 const Logger = { styles: { // 基础样式 base: 'border-radius: 3px; padding: 2px 4px; font-weight: bold;', // 不同级别的样式 info: 'background: #e6f7ff; color: #1890ff; border: 1px solid #91d5ff;', success: 'background: #f6ffed; color: #52c41a; border: 1px solid #b7eb8f;', warning: 'background: #fffbe6; color: #faad14; border: 1px solid #ffe58f;', error: 'background: #fff2f0; color: #f5222d; border: 1px solid #ffccc7;', debug: 'background: #f5f5f5; color: #666; border: 1px solid #d9d9d9;' }, formatValue(value) { if (typeof value === 'object' && value !== null) { return JSON.stringify(value, null, 2); } return String(value); }, getIcon(type) { const icons = { info: '🔵', success: '✅', warning: '⚠️', error: '❌', debug: '🔍' }; return icons[type] || '📝'; }, log(message, data = null, type = 'info') { const timestamp = new Date().toLocaleTimeString(); const icon = this.getIcon(type); console.log( `%c${icon} ${timestamp}%c ${message}`, this.styles.base + this.styles[type], 'color: #666;' ); if (data !== null) { if (typeof data === 'object') { console.log( '%c📊 详细信息:', 'color: #666; font-style: italic;' ); console.table(data); } else { console.log( '%c📊 详细信息:%c ' + this.formatValue(data), 'color: #666; font-style: italic;', 'color: #1890ff;' ); } } }, info(message, data = null) { this.log(message, data, 'info'); }, success(message, data = null) { this.log(message, data, 'success'); }, warning(message, data = null) { this.log(message, data, 'warning'); }, error(message, data = null) { this.log(message, data, 'error'); }, debug(message, data = null) { this.log(message, data, 'debug'); } }; // 全局配置 const CONFIG = { API_BASE_URL: 'https://api.moiu.cn', MAX_RETRY_COUNT: 3, RETRY_DELAY: 1000, TOAST_DURATION: 3000 }; // Toast 提示系统 class KDownToast { constructor() { this.container = null; this.queue = []; this.activeToasts = new Set(); this.initContainer(); } // 初始化 Toast 容器 initContainer() { if (!this.container) { this.container = document.createElement('div'); this.container.className = 'kdown-toast-container'; document.body.appendChild(this.container); } return this.container; } // 创建单个 Toast 元素 createToast(message, type = 'info') { const toast = document.createElement('div'); toast.className = `kdown-toast ${type}`; const content = document.createElement('div'); content.className = 'kdown-toast-content'; const icon = document.createElement('div'); icon.className = 'kdown-toast-icon'; const text = document.createElement('div'); text.className = 'kdown-toast-text'; text.innerHTML = message.replace(/\n/g, '
'); content.appendChild(icon); content.appendChild(text); toast.appendChild(content); return toast; } // 显示 Toast show(message, type = 'info', duration = 3000) { const toast = this.createToast(message, type); this.container.appendChild(toast); // 强制重排以确保动画正常 toast.offsetHeight; // 添加显示类 requestAnimationFrame(() => { toast.classList.add('show'); }); // 设置自动移除 setTimeout(() => { toast.classList.add('hide'); toast.addEventListener('transitionend', () => { if (toast.parentNode) { toast.parentNode.removeChild(toast); } }, { once: true }); }, duration); } } // 创建 Toast 实例 const toast = new KDownToast(); // 添加加载动画组件 class LoadingIndicator { constructor() { this.element = null; this.createLoadingElement(); } createLoadingElement() { this.element = document.createElement('div'); this.element.className = 'kdown-loading'; this.element.innerHTML = `
准备就绪...
`; // 添加样式 GM_addStyle(` .kdown-loading { position: fixed; left: 20px; bottom: 20px; background: rgba(0, 0, 0, 0.8); color: white; padding: 10px 15px; border-radius: 4px; z-index: 9999; font-size: 13px; } .kdown-loading-content { display: flex; align-items: center; gap: 10px; } .kdown-loading-spinner { width: 16px; height: 16px; border: 2px solid #fff; border-top-color: transparent; border-radius: 50%; animation: kdown-spin 1s linear infinite; } @keyframes kdown-spin { to { transform: rotate(360deg); } } `); } show() { if (!document.body.contains(this.element)) { document.body.appendChild(this.element); } } hide() { if (document.body.contains(this.element)) { this.element.remove(); } } updateText(text) { const textElement = this.element.querySelector('.kdown-loading-text'); if (textElement) { textElement.textContent = text; } } showSuccess() { const content = this.element.querySelector('.kdown-loading-content'); if (content) { content.innerHTML = `
初始化完成
`; this.element.classList.add('success'); } } } // 分享链接管理器 class ShareLinkManager { constructor() { this.shareLinks = GM_getValue('share_links', {}); this.loading = new LoadingIndicator(); } // 生成4位随机密码 generatePassword() { const chars = '0123456789abcdefghijklmnopqrstuvwxyz'; let pwd = ''; for (let i = 0; i < 4; i++) { pwd += chars.charAt(Math.floor(Math.random() * chars.length)); } return pwd; } // 获取分享链接 async getShareLink(fileIds) { try { const key = this.generateKey(fileIds); const cachedLink = this.shareLinks[key]; if (cachedLink && Date.now() - cachedLink.timestamp < 7 * 24 * 60 * 60 * 1000) { Logger.info('使用缓存的分享链接'); return cachedLink; } this.loading.show(); this.loading.updateText('正在创建分享链接...'); const cookies = await getCookieObject(); if (!cookies || !cookies.BDUSS) { throw new Error('获取Cookie失败或BDUSS无效'); } const bdstoken = getBdstoken(); if (!bdstoken) { throw new Error('获取bdstoken失败'); } // 生成4位随机密码 const pwd = this.generatePassword(); const response = await fetch('/share/set?channel=chunlei&clienttype=0&web=1&channel=chunlei&web=1&app_id=250528&bdstoken=' + bdstoken, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, body: new URLSearchParams({ schannel: 4, channel_list: '[]', period: 0, pwd: '0000', // 添加密码参数 fid_list: JSON.stringify(fileIds) }) }); const result = await response.json(); if (result.errno === 0) { const shareInfo = { url: result.link, pwd: '0000', // 使用生成的密码 timestamp: Date.now() }; this.saveShareLink(fileIds, shareInfo); this.loading.hide(); return shareInfo; } else { throw new Error(result.show_msg || '创建分享链接失败'); } } catch (error) { this.loading.hide(); throw error; } } // 保存分享链接 saveShareLink(fileIds, shareInfo) { const key = this.generateKey(fileIds); this.shareLinks[key] = shareInfo; this.saveToStorage(); } // 生成缓存key generateKey(fileIds) { return Array.isArray(fileIds) ? fileIds.sort().join(',') : fileIds; } // 保存到GM存储 saveToStorage() { GM_setValue('share_links', this.shareLinks); } // 清理过期的分享链接 cleanExpiredLinks() { const now = Date.now(); const expirationTime = 7 * 24 * 60 * 60 * 1000; // 7天 let hasChanges = false; Object.keys(this.shareLinks).forEach(key => { if (now - this.shareLinks[key].timestamp > expirationTime) { delete this.shareLinks[key]; hasChanges = true; } }); if (hasChanges) { this.saveToStorage(); } } // 清除所有分享链接 clearAllLinks() { this.shareLinks = {}; this.saveToStorage(); Logger.info('已清除所有分享链接缓存'); } } // 修改getCookieObject函数 async function getCookieObject() { try { Logger.info('开始获取Cookie...'); const cookies = await GM_cookie_wrapper.list({ domain: '.baidu.com' }); // Logger.info('获取到的所有Cookie', cookies.map(c => c.name)); const cookieObj = {}; cookies.forEach(cookie => { cookieObj[cookie.name] = cookie.value; }); if (!cookieObj['BDUSS']) { Logger.warning('未找到BDUSS,尝试其他方式获取...'); const baiduyunData = localStorage.getItem('baiduyunData'); if (baiduyunData) { try { const data = JSON.parse(baiduyunData); if (data.BDUSS) { cookieObj['BDUSS'] = data.BDUSS; Logger.success('从localStorage获取BDUSS成功'); } } catch (e) { Logger.error('解析localStorage数据失败', e); } } } else { Logger.success('成功获取到BDUSS'); } return cookieObj; } catch (error) { Logger.error('获取Cookie失败', error); Logger.info('尝试从document.cookie获取...'); try { const docCookies = document.cookie.split(';'); const cookieObj = {}; docCookies.forEach(cookie => { const [name, value] = cookie.trim().split('='); if (name) cookieObj[name] = value; }); Logger.info('从document.cookie获取的Cookie', cookieObj); return cookieObj; } catch (e) { Logger.error('从document.cookie获取失败', e); return {}; } } } // 获取bdstoken function getBdstoken() { try { const token = locals_wrapper.get('bdstoken'); if (token) { return token; } const bdstokenMatch = document.body.innerHTML.match(/bdstoken[\s]?=[\s]?['"]([^'"]+)['"]/); if (bdstokenMatch && bdstokenMatch[1]) { return bdstokenMatch[1]; } const urlMatch = location.href.match(/bdstoken=([^&]+)/); if (urlMatch && urlMatch[1]) { return urlMatch[1]; } Logger.error('无法获取bdstoken'); return ''; } catch (e) { Logger.error('获取bdstoken失败', e); return ''; } } // 云端加速管理器 class CloudAccelerationManager { constructor() { this.shareLinkManager = new ShareLinkManager(); this.toast = new KDownToast(); this.loading = new LoadingIndicator(); } async accelerateDownload(files) { try { this.loading.show(); this.loading.updateText('正在创建分享链接...'); // 创建分享链接 const shareInfo = await this.createShareLink(files); if (!shareInfo) { throw new Error('创建分享链接失败'); } this.loading.updateText('正在获取加速链接...'); const result = await this.getAcceleratedLinks(shareInfo.shorturl, shareInfo.pwd); if (result.status === 1 && result.data) { this.loading.showSuccess(); return { filename: result.data.filename, filesize: result.data.filesize, dlink: result.data.dlink, urls: result.data.urls || [], ua: result.data.ua, filemd5: result.data.filemd5 }; } throw new Error(result.msg || '获取加速链接失败'); } catch (error) { Logger.error('云端加速失败', error); this.toast.show(error.message, 'error'); this.loading.hide(); return null; } } async createShareLink(files) { const fileIds = files.map(f => f.fs_id); // 检查缓存 const cachedLink = this.shareLinkManager.getShareLink(fileIds); if (cachedLink) { Logger.info('使用缓存的分享链接'); return cachedLink; } try { const cookies = await getCookieObject(); if (!cookies || !cookies.BDUSS) { throw new Error('获取Cookie失败或BDUSS无效'); } Logger.info('成功获取Cookie'); const bdstoken = await getBdstoken(); if (!bdstoken) { throw new Error('获取bdstoken失败'); } Logger.info('成功获取bdstoken'); const cookieString = Object.entries(cookies) .map(([k, v]) => `${k}=${v}`) .join('; '); return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'POST', url: '/share/set?channel=chunlei&clienttype=0&web=1&app_id=250528&bdstoken=' + bdstoken, headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Cookie': cookieString }, data: `fid_list=[${fileIds.join(',')}]&schannel=4&channel_list=[]&period=0&pwd=0000`, responseType: 'json', onload: (response) => { try { const result = response.response; Logger.info('分享链接API响应', result); if (result.errno === 0) { this.shareLinkManager.saveShareLink(fileIds, result); Logger.success('创建分享链接成功'); resolve(result); } else { reject(new Error(result.show_msg || `创建分享链接失败(errno: ${result.errno})`)); } } catch (error) { Logger.error('解析响应失败', error); reject(error); } }, onerror: (error) => { Logger.error('请求失败', error); reject(new Error('网络请求失败')); } }); }); } catch (error) { Logger.error('创建分享链接失败', error); throw error; } } } // 下载处理函数 async function handleDownload(fsId, fileName, fileSize, mode = 'local') { Logger.info('开始处理下载', { fsId, fileName, fileSize, mode }); try { if (mode === 'cloud') { const cloudManager = new CloudAccelerationManager(); const files = [{ fs_id: fsId, server_filename: fileName, size: fileSize }]; const result = await cloudManager.accelerateDownload(files); if (result && result.length > 0) { const downloadInfo = result[0]; Logger.success('获取加速链接成功', { filename: downloadInfo.filename, filesize: downloadInfo.filesize || fileSize, ua: downloadInfo.ua }); // 使用第一个可用的链接 const downloadUrl = downloadInfo.urls && downloadInfo.urls.length > 0 ? downloadInfo.urls[0] : downloadInfo.dlink; // 创建下载任务 const downloadTask = { url: downloadUrl, filename: downloadInfo.filename, headers: { 'User-Agent': downloadInfo.ua || navigator.userAgent } }; // 开始下载 GM_xmlhttpRequest({ method: 'GET', url: downloadTask.url, headers: downloadTask.headers, responseType: 'blob', onload: function(response) { if (response.status === 200) { const blob = new Blob([response.response], { type: 'application/octet-stream' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = downloadTask.filename; document.body.appendChild(a); a.click(); URL.revokeObjectURL(url); document.body.removeChild(a); toast.show('下载已开始', 'success'); } else { throw new Error('下载失败'); } }, onerror: function(error) { Logger.error('下载失败', error); toast.show('下载失败,请重试', 'error'); } }); return true; } return false; } // 本地下载模式 // ... existing local download code ... } catch (error) { Logger.error('下载处理失败', error); toast.show('下载处理失败: ' + error.message, 'error'); return false; } } // 模式选择对话框 function showModeSelectionDialog(files) { const dialog = document.createElement('div'); dialog.className = 'kdown-dialog mode-selection'; dialog.innerHTML = `

选择下载模式

使用浏览器默认下载方式

使用云端服务器加速下载

`; document.body.appendChild(dialog); const file = files[0]; dialog.querySelector('.mode-btn.local').onclick = () => { dialog.remove(); handleDownload(file.fs_id, file.server_filename, file.size, 'local'); }; dialog.querySelector('.mode-btn.cloud').onclick = () => { dialog.remove(); handleDownload(file.fs_id, file.server_filename, file.size, 'cloud'); }; // 添加关闭按钮和ESC关闭支持 const closeBtn = document.createElement('button'); closeBtn.className = 'close-btn'; closeBtn.innerHTML = '×'; closeBtn.onclick = () => dialog.remove(); dialog.querySelector('.dialog-content').appendChild(closeBtn); document.addEventListener('keydown', function escListener(e) { if (e.key === 'Escape') { dialog.remove(); document.removeEventListener('keydown', escListener); } }); } (function() { 'use strict'; // 初始化全局实例 const toast = new KDownToast(); const shareLinkManager = new ShareLinkManager(); const loadingIndicator = new LoadingIndicator(); // 全局变量 let fileListData = []; // 存储文件列表数据 let fileListCache = new Map(); // 存储文件夹路径对应的文件列表缓存 let currentPath = '/'; // 当前文件夹路径 let fileNameToFsid = new Map(); let shareUrlCache = new Map(); // 缓存分享链接,key为文件ID列表的排序字符串,value为分享链接 // 添加谷歌字体 const fontLink = document.createElement('link'); fontLink.href = 'https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap'; fontLink.rel = 'stylesheet'; document.head.appendChild(fontLink); // 添加外部样式 const style = GM_getResourceText('KDOWN_STYLE'); GM_addStyle(style); // 添加自定义图标样式 GM_addStyle(` /* 系统文件图标 */ .fileicon-small-exe { background-image: url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTAyNCAxMDI0IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTg3MC40IDMxNy40TDU3NiAyM2MtNi43LTYuNy0xNS43LTEwLjQtMjUuMS0xMC40SDIyNGMtMzUuMyAwLTY0IDI4LjctNjQgNjR2ODcwLjhjMCAzNS4zIDI4LjcgNjQgNjQgNjRoNTc2YzM1LjMgMCA2NC0yOC43IDY0LTY0VjM0Mi41YzAtOS40LTMuNy0xOC40LTEwLjQtMjUuMXpNNzY4IDg5NmgtNTEyVjEyOGgyNzJ2MjI0YzAgMTcuNyAxNC4zIDMyIDMyIDMyaDIwOHY1MTJ6IiBmaWxsPSIjODg5MkJGIj48L3BhdGg+PHBhdGggZD0iTTU0NCA1NzYgNDgwbC0zMiA5NmgtNDhsMTI4LTM1Mmg0OGwxMjggMzUyaC00OGwtMzItOTZ6TTUxMiAzODRsLTQ4IDE0NGg5NmwtNDgtMTQ0eiIgZmlsbD0iIzg4OTJCRiI+PC9wYXRoPjwvc3ZnPg=="); background-size: contain; background-repeat: no-repeat; background-position: center; width: 24px; height: 24px; } `); // 添加解析记录存储和管理功能 class ParseRecordManager { constructor() { this.records = GM_getValue('parse_records', []); this.itemsPerPage = 10; } addRecord(record) { Logger.debug('添加记录,原始数据:', record); // 确保必要字段存在 const newRecord = { id: 'ma' + Math.random().toString(36).substring(2, 15), fileName: record.fileName || '', fileSize: String(record.fileSize || '0'), fsId: record.fsId || '', download_ua: record.download_ua || '', path: record.path || '', md5: record.md5 || '', timestamp: record.timestamp || Date.now(), server_filename: record.server_filename || record.fileName || '', dlink: record.dlink || '' // 确保dlink字段被保存 }; Logger.debug('处理后的记录数据:', newRecord); // 检查是否已存在相同文件的记录 const existingIndex = this.records.findIndex(r => r.fsId === newRecord.fsId); if (existingIndex !== -1) { // 更新现有记录,但保留原有的非空值 const existingRecord = this.records[existingIndex]; this.records[existingIndex] = { ...existingRecord, ...newRecord, dlink: newRecord.dlink || existingRecord.dlink, // 保留非空的dlink timestamp: Date.now() // 更新时间戳 }; Logger.debug('更新现有记录:', this.records[existingIndex]); } else { // 添加新记录 this.records.unshift(newRecord); Logger.debug('添加新记录:', newRecord); } // 限制记录数量 if (this.records.length > 100) { this.records = this.records.slice(0, 100); } this.saveRecords(); return newRecord; } // 获取分页记录 getRecords(page = 1) { const start = (page - 1) * this.itemsPerPage; const end = start + this.itemsPerPage; return { records: this.records.slice(start, end), total: this.records.length, totalPages: Math.ceil(this.records.length / this.itemsPerPage) }; } // 删除记录 deleteRecord(id) { this.records = this.records.filter(record => record.id !== id); this.saveRecords(); } // 清空所有记录 clearRecords() { this.records = []; this.saveRecords(); } // 保存记录到GM存储 saveRecords() { GM_setValue('parse_records', this.records); } // 获取单个记录 getRecord(id) { return this.records.find(record => record.id === id); } // 更新记录 updateRecord(id, updates) { const index = this.records.findIndex(record => record.id === id); if (index !== -1) { this.records[index] = { ...this.records[index], ...updates, timestamp: Date.now() // 更新时间戳 }; this.saveRecords(); return this.records[index]; } return null; } } // 创建解析记录管理器实例 const recordManager = new ParseRecordManager(); // 添加下载器配置管理 const downloaderConfig = { current: GM_getValue('current_downloader', 'motrix'), presets: { motrix: { name: 'Motrix', icon: 'data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEyIDJDNi40NzcgMiAyIDYuNDc3IDIgMTJzNC40NzcgMTAgMTAgMTAgMTAtNC40NzcgMTAtMTBTMTcuNTIzIDIgMTIgMnptNC44ODkgMTMuNjM2YS43NS43NSAwIDAgMS0xLjA2MSAwbC0zLjc1LTMuNzVhLjc1Ljc1IDAgMCAxIDAtMS4wNjFsMy43NS0zLjc1YS43NS43NSAwIDEgMSAxLjA2MSAxLjA2MUwxMy41NjEgMTJsNC4zMjggNC4zMjhhLjc1Ljc1IDAgMCAxIDAgMS4wNjF6TTcuMTExIDE1LjYzNmEuNzUuNzUgMCAwIDEtMS4wNjEgMCAuNzUuNzUgMCAwIDEgMC0xLjA2MUwxMC4zNzggMTJsLTQuMzI4LTQuMzI4YS43NS43NSAwIDAgMSAxLjA2MS0xLjA2MWwzLjc1IDMuNzVhLjc1Ljc1IDAgMCAxIDAgMS4wNjFsLTMuNzUgMy43NXoiIGZpbGw9IiMwMGIyZmYiLz48L3N2Zz4=', defaultPort: 16800, url: 'http://localhost:16800/jsonrpc' }, aria2: { name: 'Aria2', icon: 'data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEyIDJDNi40NzcgMiAyIDYuNDc3IDIgMTJzNC40NzcgMTAgMTAgMTAgMTAtNC40NzcgMTAtMTBTMTcuNTIzIDIgMTIgMnptNCA5aC00VjdoLTR2NGgtNHY0aDR2NGg0di00aDR2LTR6IiBmaWxsPSIjMDBiMmZmIi8+PC9zdmc+', defaultPort: 6800, url: 'http://localhost:6800/jsonrpc' } }, settings: GM_getValue('downloader_settings', { token: '', dir: 'D:\\Downloads', ports: { motrix: 16800, aria2: 6800 } }), save() { GM_setValue('current_downloader', this.current); GM_setValue('downloader_settings', this.settings); } }; // 修改配置对话框 function showConfigDialog() { const dialog = document.createElement('div'); dialog.className = 'kdown-config-dialog'; dialog.innerHTML = `

下载器配置

×
${Object.entries(downloaderConfig.presets).map(([key, preset]) => `
${preset.name}
${preset.name}
`).join('')}
`; // 添加新样式 GM_addStyle(` .kdown-config-dialog { background: #fff; border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); width: 480px; animation: dialogFadeIn 0.3s ease; } @keyframes dialogFadeIn { from { opacity: 0; transform: translate(-50%, -48%) scale(0.96); } to { opacity: 1; transform: translate(-50%, -50%) scale(1); } } .kdown-config-header { padding: 16px 24px; border-bottom: 1px solid #f0f0f0; } .kdown-config-header h3 { margin: 0; font-size: 16px; color: #262626; } .kdown-preset-cards { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; margin-bottom: 24px; } .kdown-preset-card { display: flex; align-items: center; padding: 16px; border: 1px solid #f0f0f0; border-radius: 8px; cursor: pointer; transition: all 0.3s ease; } .kdown-preset-card:hover { border-color: #40a9ff; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); } .kdown-preset-card.active { border-color: #1890ff; background: #e6f7ff; } .preset-icon { width: 32px; height: 32px; margin-right: 12px; } .preset-icon img { width: 100%; height: 100%; } .preset-info { flex: 1; } .preset-name { font-weight: 500; margin-bottom: 4px; } .preset-port { display: flex; align-items: center; } .port-input { width: 80px; padding: 4px 8px; border: 1px solid #d9d9d9; border-radius: 4px; font-size: 13px; } .port-input:focus { border-color: #40a9ff; outline: none; box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2); } .input-wrapper { position: relative; display: flex; align-items: center; } .toggle-password, .browse-button { background: none; border: none; padding: 4px; color: #999; cursor: pointer; position: absolute; right: 8px; top: 50%; transform: translateY(-50%); transition: color 0.3s; } .toggle-password:hover, .browse-button:hover { color: #666; } .kdown-config-content { padding: 24px; } .config-item { margin-bottom: 20px; } .config-item:last-child { margin-bottom: 0; } .config-item label { display: block; margin-bottom: 8px; color: #262626; font-size: 14px; } .kdown-input { width: 100%; padding: 8px 32px 8px 12px; border: 1px solid #d9d9d9; border-radius: 4px; transition: all 0.3s; } .kdown-input:focus { border-color: #40a9ff; outline: none; box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2); } .kdown-config-footer { padding: 16px 24px; border-top: 1px solid #f0f0f0; display: flex; justify-content: flex-end; gap: 12px; } .kdown-btn { padding: 8px 16px; border-radius: 4px; font-size: 14px; cursor: pointer; transition: all 0.3s; border: none; } .kdown-btn.primary { background: #1890ff; color: white; } .kdown-btn.primary:hover { background: #40a9ff; } .kdown-btn.secondary { background: #f0f0f0; color: #262626; } .kdown-btn.secondary:hover { background: #d9d9d9; } `); // 绑定预设卡片点击事件 dialog.querySelectorAll('.kdown-preset-card').forEach(card => { card.addEventListener('click', (e) => { if (!e.target.classList.contains('port-input')) { const preset = card.dataset.preset; dialog.querySelectorAll('.kdown-preset-card').forEach(c => { c.classList.toggle('active', c === card); }); downloaderConfig.current = preset; } }); }); // 绑定端口输入事件 dialog.querySelectorAll('.port-input').forEach(input => { input.addEventListener('change', (e) => { const preset = e.target.dataset.preset; const port = parseInt(e.target.value); if (port >= 1 && port <= 65535) { if (!downloaderConfig.settings.ports) { downloaderConfig.settings.ports = {}; } downloaderConfig.settings.ports[preset] = port; } else { e.target.value = downloaderConfig.settings.ports?.[preset] || downloaderConfig.presets[preset].defaultPort; toast.show('端口号必须在1-65535之间', 'error', 2000); } }); }); // 绑定密码显示切换 const togglePassword = dialog.querySelector('.toggle-password'); const rpcTokenInput = dialog.querySelector('#rpcToken'); togglePassword.addEventListener('click', () => { const type = rpcTokenInput.type === 'password' ? 'text' : 'password'; rpcTokenInput.type = type; togglePassword.innerHTML = type === 'password' ? '' : ''; }); // 保存配置 dialog.querySelector('#saveConfig').onclick = () => { downloaderConfig.settings.token = dialog.querySelector('#rpcToken').value; downloaderConfig.settings.dir = dialog.querySelector('#downloadDir').value; // 更新下载器URL Object.keys(downloaderConfig.presets).forEach(key => { const port = downloaderConfig.settings.ports?.[key] || downloaderConfig.presets[key].defaultPort; downloaderConfig.presets[key].url = `http://localhost:${port}/jsonrpc`; }); downloaderConfig.save(); dialog.remove(); toast.show('配置已保存', 'success', 2000); }; dialog.querySelector('#cancelConfig').onclick = () => dialog.remove(); dialog.querySelector('.kdown-config-close').onclick = () => dialog.remove(); document.body.appendChild(dialog); } // 添加辅助函数用于处理 headers function sanitizeHeaders(headers) { const sanitized = {}; for (const [key, value] of Object.entries(headers)) { // 如果值包含非 ASCII 字符,进行 encodeURIComponent if (/[^\x00-\x7F]/.test(value)) { sanitized[key] = encodeURIComponent(value); } else { sanitized[key] = value; } } return sanitized; } // 修改 sendToDownloader 函数 async function sendToDownloader(record) { const preset = downloaderConfig.presets[downloaderConfig.current]; if (!preset) { toast.show('请先配置下载器', 'error', 3000); return; } // 检查下载链接是否存在 if (!record.dlink) { toast.show('下载链接不存在', 'error', 3000); return; } try { const headers = sanitizeHeaders({ 'User-Agent': record.download_ua || '' }); const jsonrpc = { jsonrpc: '2.0', id: Date.now(), method: 'aria2.addUri', params: [[record.dlink], { dir: downloaderConfig.settings.dir, header: Object.entries(headers).map(([k, v]) => `${k}: ${v}`).filter(h => h.endsWith(': ') === false), out: record.server_filename || record.fileName }] }; if (downloaderConfig.settings.token) { jsonrpc.params.unshift('token:' + downloaderConfig.settings.token); } const response = await fetch(preset.url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(jsonrpc) }); const result = await response.json(); if (result.error) { throw new Error(result.error.message); } toast.show('已发送到下载器', 'success', 2000); } catch (error) { Logger.error('发送到下载器失败', error); toast.show('发送失败: ' + error.message, 'error', 3000); } } // 修改createParseRecordPage函数,将updateRecordList提升为全局函数 let updateRecordList; // 声明为全局变量 function createParseRecordPage() { const page = document.createElement('div'); page.className = 'kdown-parse-record-page'; let currentPage = 1; // 定义updateRecordList函数 updateRecordList = function() { const { records, total, totalPages } = recordManager.getRecords(currentPage); page.innerHTML = `

解析记录

共 ${total} 条记录
${records.length === 0 ? ` ` : records.map(record => ` `).join('')}
文件名 大小 解析时间 操作

暂无解析记录

${record.fileName}
${record.fileSize || '-'} ${new Date(record.timestamp).toLocaleString()}
${totalPages > 1 ? `
第 ${currentPage}/${totalPages} 页
` : ''} `; // 绑定事件处理 const refreshBtn = page.querySelector('.refresh-btn'); if (refreshBtn) { refreshBtn.onclick = () => { // 刷新整个页面 window.location.reload(); }; } const configBtn = page.querySelector('.config-btn'); if (configBtn) { configBtn.onclick = () => { showConfigDialog(); }; } const clearBtn = page.querySelector('.kdown-clear-btn'); if (clearBtn && !clearBtn.disabled) { clearBtn.onclick = () => { showConfirmDialog({ title: '清空解析记录', content: '确定要清空所有解析记录吗?此操作不可恢复。', onConfirm: () => { recordManager.clearRecords(); updateRecordList(); toast.show('解析记录已清空', 'success', 2000); } }); }; } // 绑定操作按钮事件 page.querySelectorAll('.kdown-action-btn').forEach(btn => { const row = btn.closest('tr'); if (!row) return; const recordId = row.dataset.id; const record = records.find(r => r.id === recordId); if (!record) return; if (btn.classList.contains('send-btn')) { btn.onclick = () => sendToDownloader(record); } else if (btn.classList.contains('copy-btn')) { btn.onclick = () => { if (!record.dlink) { toast.show('下载链接不存在', 'error', 2000); return; } // 使用Clipboard API复制 try { navigator.clipboard.writeText(record.dlink) .then(() => toast.show('下载链接已复制', 'success', 2000)) .catch(() => { // 如果Clipboard API失败,尝试使用传统方法 const textarea = document.createElement('textarea'); textarea.value = record.dlink; document.body.appendChild(textarea); textarea.select(); try { document.execCommand('copy'); toast.show('下载链接已复制', 'success', 2000); } catch (err) { toast.show('复制失败,请手动复制', 'error', 2000); } document.body.removeChild(textarea); }); } catch (error) { toast.show('复制失败,请手动复制', 'error', 2000); } }; } else if (btn.classList.contains('delete-btn')) { btn.onclick = () => { showConfirmDialog({ title: '删除记录', content: `确定要删除文件 "${record.fileName}" 的解析记录吗?`, onConfirm: () => { recordManager.deleteRecord(recordId); updateRecordList(); toast.show('记录已删除', 'success', 2000); } }); }; } }); // 分页按钮事件 page.querySelectorAll('.kdown-page-btn').forEach(btn => { btn.onclick = () => { if (btn.dataset.action === 'prev' && currentPage > 1) { currentPage--; updateRecordList(); } else if (btn.dataset.action === 'next' && currentPage < totalPages) { currentPage++; updateRecordList(); } }; }); }; // 初始化显示 updateRecordList(); return page; } // 添加新的样式 GM_addStyle(` /* 解析记录页面样式 */ .kdown-parse-record-page { padding: 24px; background: white; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); margin: 16px; } .kdown-record-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; padding-bottom: 16px; border-bottom: 1px solid #f0f0f0; } .kdown-record-title { display: flex; align-items: center; gap: 12px; } .kdown-record-title h2 { margin: 0; font-size: 20px; color: #262626; font-weight: 500; } .record-icon { color: #1890ff; } .kdown-record-actions { display: flex; align-items: center; justify-content: space-between; gap: 16px; } .record-count { color: #666; font-size: 14px; } .kdown-clear-btn { display: flex; align-items: center; gap: 6px; padding: 6px 12px; background: #ff4d4f; color: white; border: none; border-radius: 4px; cursor: pointer; transition: all 0.3s; } .kdown-clear-btn:hover { background: #ff7875; } .kdown-clear-btn:disabled { background: #d9d9d9; cursor: not-allowed; } .kdown-record-list table { width: 100%; border-collapse: separate; border-spacing: 0; border-radius: 8px; overflow: hidden; } .kdown-record-list th { background: #fafafa; padding: 16px; text-align: left; font-weight: 500; color: #262626; border-bottom: 1px solid #f0f0f0; } .kdown-record-list td { padding: 16px; border-bottom: 1px solid #f0f0f0; } .kdown-record-list tr:last-child td { border-bottom: none; } .filename-wrapper { display: flex; align-items: center; gap: 8px; } .filename { color: #262626; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .action-buttons { display: flex; gap: 8px; } .kdown-action-btn { padding: 6px; background: transparent; border: none; border-radius: 4px; cursor: pointer; color: #666; transition: all 0.3s; } .kdown-action-btn:hover { background: #f5f5f5; color: #1890ff; } .empty-state { display: flex; flex-direction: column; align-items: center; padding: 48px 0; color: #999; } .empty-state svg { margin-bottom: 16px; } .empty-state p { margin: 0; font-size: 14px; } .kdown-pagination { display: flex; justify-content: center; align-items: center; gap: 16px; margin-top: 24px; padding-top: 16px; border-top: 1px solid #f0f0f0; } .kdown-page-btn { display: flex; align-items: center; gap: 4px; padding: 8px 16px; background: white; border: 1px solid #d9d9d9; border-radius: 4px; cursor: pointer; transition: all 0.3s; } .kdown-page-btn:hover:not(:disabled) { border-color: #1890ff; color: #1890ff; } .kdown-page-btn:disabled { background: #f5f5f5; cursor: not-allowed; color: #d9d9d9; } .kdown-page-info { color: #666; font-size: 14px; } /* 导航菜单样式优化 */ .kdown-nav-menu { display: flex; background: white; padding: 0 24px; border-bottom: 1px solid #f0f0f0; margin: 16px 16px 0; border-radius: 8px 8px 0 0; } .kdown-nav-item { padding: 16px 24px; color: #595959; text-decoration: none; position: relative; transition: all 0.3s; } .kdown-nav-item:hover { color: #1890ff; } .kdown-nav-item.active { color: #1890ff; } .kdown-nav-item.active::after { content: ''; position: absolute; bottom: 0; left: 0; width: 100%; height: 2px; background: #1890ff; } `); // 添加样式 GM_addStyle(` /* 文件列表样式 */ .kdown-file-list-content { max-height: 300px; overflow-y: auto; } .kdown-file-list-content::-webkit-scrollbar { width: 6px; } .kdown-file-list-content::-webkit-scrollbar-thumb { background: #ccc; border-radius: 3px; } .kdown-file-list-content::-webkit-scrollbar-track { background: #f5f5f5; } .kdown-mode-option:hover { color: #2196f3; } .kdown-mode-option input[type="radio"]:checked + span { color: #2196f3; } .kdown-mode-option input[type="radio"]:checked + span svg path { fill: #2196f3; } /* 工具提示样式 */ .tooltip-trigger { position: relative; } .tooltip-trigger:hover::after { content: attr(title); position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); padding: 6px 10px; background: rgba(0, 0, 0, 0.75); color: white; font-size: 12px; white-space: nowrap; border-radius: 4px; margin-bottom: 5px; z-index: 1000; } .tooltip-trigger:hover::before { content: ''; position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); border: 5px solid transparent; border-top-color: rgba(0, 0, 0, 0.75); margin-bottom: -5px; z-index: 1000; } /* 导航菜单样式 */ .kdown-nav-menu { display: flex; background: #f5f5f5; padding: 10px; border-bottom: 1px solid #e8e8e8; margin-bottom: 15px; } .kdown-nav-item { padding: 8px 15px; color: #666; text-decoration: none; margin-right: 10px; border-radius: 4px; transition: all 0.3s; } .kdown-nav-item:hover { background: #e8e8e8; } .kdown-nav-item.active { background: #1890ff; color: white; } /* 按钮样式 */ .kdown-action-btn { padding: 4px 8px; margin: 0 4px; background: transparent; border: none; cursor: pointer; color: #666; transition: color 0.3s; } .kdown-action-btn:hover { color: #00b2ff; } .kdown-action-btn svg { vertical-align: middle; } .kdown-clear-btn { padding: 6px 12px; background: #ff4d4f; color: white; border: none; border-radius: 4px; cursor: pointer; } .kdown-clear-btn:disabled { background: #d9d9d9; cursor: not-allowed; } /* 配置对话框样式 */ .kdown-config-dialog { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); z-index: 10000; width: 500px; } .kdown-config-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; } .kdown-config-close { cursor: pointer; font-size: 24px; color: #999; } .kdown-config-section { margin-bottom: 20px; } .config-item { margin: 10px 0; display: flex; align-items: center; } .config-item label { width: 80px; color: #666; } .config-item input { flex: 1; padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; } /* 分页样式 */ .kdown-pagination { display: flex; justify-content: center; align-items: center; margin-top: 20px; gap: 10px; } .kdown-page-btn { padding: 6px 12px; background: white; border: 1px solid #d9d9d9; border-radius: 4px; cursor: pointer; } .kdown-page-btn:disabled { background: #f5f5f5; cursor: not-allowed; } .kdown-page-info { color: #666; } /* 其他样式 */ .action-cell { white-space: nowrap; } .no-records { text-align: center; color: #999; padding: 40px 0; } .loading-spinner { display: inline-block; width: 12px; height: 12px; margin-right: 8px; border: 2px solid #fff; border-top-color: transparent; border-radius: 50%; animation: kdown-spin 1s linear infinite; } @keyframes kdown-spin { to { transform: rotate(360deg); } } .kdown-alert-button.primary:disabled { opacity: 0.7; cursor: not-allowed; } .kdown-alert-button.primary:disabled .loading-spinner { opacity: 0.8; } .kdown-btn { padding: 6px 16px; border-radius: 4px; border: none; cursor: pointer; } .kdown-btn.primary { background: #00b2ff; color: white; } .kdown-btn.secondary { background: #f5f5f5; color: #666; } .kdown-select { flex: 1; padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; background: white; } .kdown-input { flex: 1; padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; } .kdown-config-dialog { width: 400px; } .kdown-config-section { padding: 20px; } .config-item { margin-bottom: 15px; } .config-item label { display: block; margin-bottom: 5px; color: #666; } .kdown-config-footer { padding: 15px 20px; border-top: 1px solid #eee; display: flex; justify-content: flex-end; gap: 10px; } `); // 添加GM_cookie兼容性处理 const GM_cookie_wrapper = { list: function(details) { return new Promise((resolve, reject) => { try { if (typeof GM_cookie !== 'undefined') { GM_cookie.list(details, resolve); } else if (typeof GM !== 'undefined' && GM.cookie) { GM.cookie.list(details).then(resolve).catch(reject); } else { // 如果都不支持,返回从document.cookie解析的结果 const cookies = document.cookie.split(';').map(cookie => { const [name, value] = cookie.trim().split('='); return { name: name, value: value, domain: details.domain || window.location.hostname }; }); resolve(cookies); } } catch (error) { reject(error); } }); }, set: function(details) { return new Promise((resolve, reject) => { try { if (typeof GM_cookie !== 'undefined') { GM_cookie.set(details, resolve); } else if (typeof GM !== 'undefined' && GM.cookie) { GM.cookie.set(details).then(resolve).catch(reject); } else { document.cookie = `${details.name}=${details.value}`; resolve(); } } catch (error) { reject(error); } }); }, delete: function(details) { return new Promise((resolve, reject) => { try { if (typeof GM_cookie !== 'undefined') { GM_cookie.delete(details, resolve); } else if (typeof GM !== 'undefined' && GM.cookie) { GM.cookie.delete(details).then(resolve).catch(reject); } else { document.cookie = `${details.name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT`; resolve(); } } catch (error) { reject(error); } }); } }; // 修改gmCookie函数使用包装器 function gmCookie(domain) { const cookieHandler = { $changes: new Set(), $alldone() { const promises = Array.from(this.$changes).map(([action, ...args]) => { return GM_cookie_wrapper[action](...args); }); this.$changes.clear(); return Promise.all(promises); } }; return GM_cookie_wrapper.list({ domain }).then(cookies => { cookies.forEach(cookie => { Object.defineProperty(cookieHandler, cookie.name, { enumerable: true, configurable: true, get() { return cookie; }, set(value) { if (value === undefined) { this.$changes.add(['delete', { name: cookie.name, domain }]); } else { this.$changes.add(['set', { ...cookie, ...value, domain }]); } } }); }); return cookieHandler; }); } // 添加导航菜单 function initializeMenu() { Logger.debug('开始初始化菜单'); // 创建容器 const container = document.createElement('div'); container.className = 'kdown-container'; const pageContainer = document.createElement('div'); pageContainer.className = 'kdown-page-container'; const navMenu = document.createElement('div'); navMenu.className = 'kdown-nav-menu'; navMenu.innerHTML = ` 首页 解析记录 每日签到 关于 💰 积分: -- `; // 添加导航菜单和页面容器 container.appendChild(navMenu); container.appendChild(pageContainer); // 更新积分显示 async function updateCreditsDisplay() { try { const creditsManager = new UserCreditsManager(); const credits = await creditsManager.getCredits(); const creditsElement = document.getElementById('kdown-credits'); if (creditsElement) { creditsElement.textContent = credits; } } catch (error) { Logger.error('获取积分失败:', error); } } // 定期更新积分显示 updateCreditsDisplay(); setInterval(updateCreditsDisplay, 60000); // 每分钟更新一次 // 添加到页面 const mainContent = document.querySelector('.KPDwCE'); if (mainContent) { mainContent.parentNode.insertBefore(container, mainContent); // 添加菜单点击事件 navMenu.addEventListener('click', (e) => { const navItem = e.target.closest('.kdown-nav-item'); if (!navItem) return; e.preventDefault(); // 阻止默认的链接行为 // 如果点击的是积分菜单项,显示提示信息 if (navItem.dataset.page === 'credits') { toast.info('看什么看,这是个显示文本'); return; } // 更新激活状态 navMenu.querySelectorAll('.kdown-nav-item').forEach(item => { item.classList.remove('active'); }); navItem.classList.add('active'); // 更新页面内容 const page = navItem.dataset.page; updatePageContent(page); }); Logger.success('菜单初始化完成'); } else { Logger.error('未找到主内容区域'); } } // 修改拦截器初始化函数 function initializeInterceptors() { const originalXHR = unsafeWindow.XMLHttpRequest; unsafeWindow.XMLHttpRequest = function() { const xhr = new originalXHR(); const originalOpen = xhr.open; const originalSend = xhr.send; xhr.open = function() { this.requestURL = arguments[1]; if (this.requestURL?.includes('/api/list')) { Logger.info('拦截到文件列表请求', this.requestURL); try { let dir = '/'; if (this.requestURL.includes('?')) { const urlParams = new URLSearchParams(this.requestURL.split('?')[1]); dir = urlParams.get('dir') || '/'; dir = decodeURIComponent(dir); } Logger.info('当前请求目录', dir); fileListManager.setCurrentPath(dir); } catch (e) { Logger.error('解析URL参数失败', e); fileListManager.setCurrentPath('/'); } } return originalOpen.apply(this, arguments); }; xhr.send = function() { if (this.requestURL?.includes('/api/list')) { this.addEventListener('readystatechange', function() { if (this.readyState === 4 && this.status === 200) { try { const response = JSON.parse(this.responseText); if (response.errno === 0 && Array.isArray(response.list)) { let path = '/'; if (this.requestURL.includes('?')) { const urlParams = new URLSearchParams(this.requestURL.split('?')[1]); path = urlParams.get('dir') || '/'; path = decodeURIComponent(path); } Logger.success('文件列表数据获取成功', { 路径: path, 文件数量: response.list.length }); fileListManager.updateCache(path, response.list); } } catch (e) { Logger.error('解析文件列表失败', e.message); } } }); } return originalSend.apply(this, arguments); }; return xhr; }; // 监听路径变化 const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.type === 'childList' || mutation.type === 'attributes') { const pathElement = document.querySelector('.KLxwHe'); if (pathElement) { try { const currentPath = new URLSearchParams(window.location.search).get('dir') || '/'; const decodedPath = decodeURIComponent(currentPath); const currentCachedPath = fileListManager.getCurrentPath(); if (decodedPath !== currentCachedPath) { Logger.info('检测到路径变化', { 旧路径: currentCachedPath, 新路径: decodedPath }); const cachedList = fileListManager.getCache(decodedPath); if (!cachedList) { Logger.debug('路径无缓存,准备刷新...'); const refreshBtn = document.querySelector('.nd-button-refresh'); if (refreshBtn) { Logger.info('触发刷新按钮点击'); refreshBtn.click(); } } else { Logger.success('使用缓存的文件列表', { 路径: decodedPath, 缓存数量: cachedList.length }); fileListManager.setCurrentPath(decodedPath); } } } catch (e) { Logger.error('解析当前URL失败', e); } } } } }); const pathContainer = document.querySelector('.KLxwHe')?.parentElement; if (pathContainer) { observer.observe(pathContainer, { childList: true, subtree: true, attributes: true }); } } // 修改getFileDetails函数 function getFileDetails(element) { const currentPath = fileListManager.getCurrentPath(); Logger.info('开始解析文件元素', { 元素类名: element.className, 当前路径: currentPath }); const nameElement = element.querySelector('.file-name .text a'); const sizeElement = element.querySelector('.ebjXPV'); const timeElement = element.querySelector('.myej2rO'); const iconElement = element.querySelector('.led3eB'); const fileName = nameElement ? nameElement.textContent.trim() : ''; const fs_id = fileListManager.getFsId(currentPath, fileName); if (!fs_id) { Logger.warning('未找到文件fsid', { 文件名: fileName, 当前路径: currentPath }); } // 获取文件扩展名 const fileExt = fileName.split('.').pop().toLowerCase(); // 根据文件类型返回对应的图标类名 function getIconClass(isFolder, fileExt) { if (isFolder) { return 'dir-small'; } // 特殊系统图标 if (fileExt.toLowerCase() === 'exe') { return 'fileicon-small-exe'; } // 普通文件图标 return `fileicon-small-${fileExt.toLowerCase()}`; } const isDir = isFolder(element); const iconClass = getIconClass(isDir, fileExt); Logger.info('文件详情收集完成', { 文件名: fileName, fs_id: fs_id, 是否文件夹: isDir, 当前路径: currentPath }); return { icon: iconClass, name: fileName, isFolder: isDir, size: sizeElement ? sizeElement.textContent.trim() : '', time: timeElement ? timeElement.textContent.trim() : '', element: element, fs_id: fs_id }; } // 判断是否为文件夹 function isFolder(element) { // 更新文件夹判断逻辑 const iconElement = element.querySelector('.led3eB'); return iconElement && ( iconElement.classList.contains('fileicon-small-dir') || iconElement.classList.contains('dir-small') || iconElement.classList.contains('fileicon-small-folder') ); } // 添加 API_URL 常量定义 const API_URL = 'https://api.moiu.cn/one_api.php'; // 修改文件列表弹窗的 HTML 结构 function showFileListDialog(files) { Logger.info('准备显示文件列表', files); let shareInfo = null; // 用于存储分享链接信息 const dialog = document.createElement('div'); dialog.className = 'kdown-file-list'; const totalFolders = files.filter(f => f.isFolder).length; dialog.innerHTML = `

下载文件列表 (共选择${files.length}个文件)

×
${files.map(file => ` `).join('')}
文件名 类型 大小 修改时间
${file.name || '未知文件名'} ${file.isFolder ? '文件夹' : '文件'} ${file.size || '-'} ${file.time || '-'}
`; // 添加样式 GM_addStyle(` .kdown-file-list { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); width: 90%; max-width: 800px; z-index: 9999; } .kdown-file-list-header { padding: 16px; border-bottom: 1px solid #f0f0f0; display: flex; justify-content: space-between; align-items: flex-start; } .kdown-file-list-title { flex: 1; } .kdown-file-list-title h3 { margin: 0; font-size: 16px; color: #333; } .kdown-share-url { margin-top: 8px; } .share-url-container { display: flex; align-items: center; gap: 8px; background: #f5f5f5; padding: 4px 8px; border-radius: 4px; } .share-url-label { color: #666; font-size: 13px; } .share-url-text { color: #1890ff; font-size: 13px; } .copy-btn { padding: 2px 8px; display: flex; align-items: center; gap: 4px; border: 1px solid #d9d9d9; border-radius: 4px; background: white; cursor: pointer; font-size: 12px; } .copy-btn:hover { background: #f5f5f5; } .kdown-file-list-close { font-size: 20px; color: #999; cursor: pointer; padding: 4px; margin: -4px; } .kdown-file-list-close:hover { color: #666; } `); const overlay = document.createElement('div'); overlay.className = 'kdown-overlay'; overlay.style.cssText = ` position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 9998; `; document.body.appendChild(overlay); document.body.appendChild(dialog); // 初始化下载 async function initializeDownload() { const statusText = dialog.querySelector('.status-text'); const downloadButton = dialog.querySelector('.kdown-alert-button.primary'); const shareUrlContainer = dialog.querySelector('.kdown-share-url'); const shareUrlText = dialog.querySelector('.share-url-text'); const copyButton = dialog.querySelector('.copy-btn'); let shareInfo = null; // 添加变量声明 if (!statusText || !downloadButton || !shareUrlContainer || !shareUrlText) { Logger.error('初始化失败:找不到必要的DOM元素'); return; } // 绑定复制按钮事件 if (copyButton) { copyButton.addEventListener('click', function() { const textToCopy = shareUrlText.textContent; if (textToCopy) { navigator.clipboard.writeText(textToCopy) .then(() => toast.show('链接已复制到剪贴板', 'success', 2000)) .catch(() => toast.show('复制失败,请手动复制', 'error', 2000)); } }); } try { // 获取分享链接 statusText.textContent = '正在获取分享链接...'; const cloudManager = new CloudAccelerationManager(); // 添加实例化 shareInfo = await cloudManager.createShareLink(files); if (shareInfo && shareInfo.url) { shareUrlText.textContent = shareInfo.url; shareUrlContainer.style.display = 'block'; statusText.textContent = '分享链接获取成功'; } else { throw new Error('创建分享链接失败'); } // 监听下载模式切换 const modeSelect = dialog.querySelector('.kdown-mode-select'); if (modeSelect) { modeSelect.addEventListener('change', function() { const selectedMode = this.value; if (selectedMode === 'cloud') { statusText.textContent = '准备使用云端加速模式,将消耗1积分'; if (userCredits < 1) { toast.show('积分不足,请切换到本地加速模式或充值积分', 'error', 3000); downloadButton.disabled = true; return; } } else { statusText.textContent = '准备使用本地加速模式,非SVIP用户可能限速'; } downloadButton.disabled = false; }); } // 修改下载按钮点击事件 if (downloadButton) { downloadButton.onclick = async () => { const selectedMode = dialog.querySelector('.kdown-mode-select').value; // 禁用下载按钮,显示加载状态 downloadButton.disabled = true; const originalText = downloadButton.textContent; downloadButton.innerHTML = ` 处理中... `; try { if (selectedMode === 'cloud') { if (!shareInfo) { throw new Error('分享链接未创建,请刷新重试'); } toast.show('正在使用云端加速模式解析,请稍候...', 'info', 2000); console.log('开始云端加速处理,共', files.length, '个文件'); // 使用已创建的分享链接进行云端加速处理 const acceleratedLinks = await handleCloudAcceleration(shareInfo.url, shareInfo.pwd); if (!acceleratedLinks) { throw new Error('云端加速处理失败'); } Logger.success('云端加速处理完成'); statusText.textContent = '解析完成'; toast.show('解析成功!正在前往解析日志页面...', 'success', 2000); } else { // 本地模式处理逻辑... toast.show('正在使用本地加速模式解析,请稍候...', 'info', 2000); console.log('开始处理文件下载,共', files.length, '个文件'); for (const [index, file] of files.entries()) { if (file.fs_id) { const progressText = `正在解析: ${file.name} (${index + 1}/${files.length})`; statusText.textContent = progressText; Logger.info(progressText); await handleDownload(file.fs_id, file.name, file.size, 'local'); } } Logger.success('所有文件处理完成'); statusText.textContent = '解析完成'; toast.show('解析成功!正在前往解析日志页面...', 'success', 2000); } // 关闭文件列表弹窗并跳转到解析记录页面 setTimeout(() => { dialog.remove(); overlay.remove(); // 先切换到记录页面 const recordsNavItem = document.querySelector('.kdown-nav-item[data-page="records"]'); if (recordsNavItem) { recordsNavItem.click(); } }, 1500); } catch (error) { Logger.error('处理失败', error); statusText.textContent = '处理失败: ' + error.message; toast.show(error.message, 'error'); } }; } downloadButton.disabled = false; } catch (error) { Logger.error('初始化失败', error); statusText.textContent = '初始化失败: ' + error.message; if (downloadButton) downloadButton.disabled = true; toast.show('初始化失败: ' + error.message, 'error', 3000); } } // 开始初始化检查 if (totalFolders === 0) { initializeDownload(); } // 绑定关闭事件 const closeDialog = () => { dialog.remove(); overlay.remove(); Logger.info('文件列表弹窗已关闭'); }; dialog.querySelector('.kdown-file-list-close').onclick = closeDialog; dialog.querySelector('#kdown-cancel-btn').onclick = closeDialog; return dialog; } // 获取选中的项目 function getSelectedItems() { // 使用 fkwBLaj 类来识别选中的项目 return document.querySelectorAll('.AuPKyz.fkwBLaj'); } // 检查选中项是否包含文件夹 function hasSelectedFolder() { const selectedItems = getSelectedItems(); Logger.debug('检查选中项', { selectedCount: selectedItems.length, items: Array.from(selectedItems).map(item => ({ classes: item.className, isFolder: isFolder(item) })) }); return Array.from(selectedItems).some(item => isFolder(item)); } // 统计选中的文件和文件夹数量 function countSelectedItems() { const selectedItems = getSelectedItems(); let folders = 0; let files = 0; selectedItems.forEach(item => { if (isFolder(item)) { folders++; } else { files++; } }); return { folders, files }; } // 修改下载按钮点击事件 function createKDownButton() { Logger.info('开始创建KDown按钮'); // 查找工具栏 const toolbar = document.querySelector('.QDDOQB'); if (!toolbar) { Logger.error('未找到工具栏'); return null; } // 检查是否已存在按钮 const existingButton = document.querySelector('.kdown-download'); if (existingButton) { Logger.debug('KDown按钮已存在'); return existingButton; } const downloadBtn = document.createElement('a'); downloadBtn.className = 'g-button kdown-download'; downloadBtn.setAttribute('href', 'javascript:;'); downloadBtn.setAttribute('title', 'KDown下载'); downloadBtn.innerHTML = ` KDown下载 `; // 添加下载按钮事件处理 downloadBtn.addEventListener('click', function(e) { const selectedItems = getSelectedItems(); Logger.info('下载按钮被点击', { selectedCount: selectedItems.length }); if (selectedItems.length === 0) { toast.show('请先选择要下载的文件', 'info', 2000); return; } const fileDetails = Array.from(selectedItems).map(getFileDetails); Logger.debug('获取选中文件详情', fileDetails); showFileListDialog(fileDetails); }); // 将按钮添加到工具栏的第一个位置 const firstChild = toolbar.firstChild; if (firstChild) { toolbar.insertBefore(downloadBtn, firstChild); } else { toolbar.appendChild(downloadBtn); } Logger.success('KDown按钮创建完成'); return downloadBtn; } // 修改observeFileSelection函数,添加按钮重新创建的逻辑 function observeFileSelection() { // 防止重复添加观察器 if (window._kdownObserverInitialized) return; window._kdownObserverInitialized = true; // 创建一个观察器来监视工具栏的变化 const toolbarObserver = new MutationObserver((mutations) => { const toolbar = document.querySelector('.QDDOQB'); if (toolbar && !document.querySelector('.kdown-download')) { createKDownButton(); } }); // 观察body元素,因为工具栏可能会动态加载 toolbarObserver.observe(document.body, { childList: true, subtree: true }); // 创建一个观察器来监视文件选择的变化 const observer = new MutationObserver((mutations) => { let shouldUpdate = false; for (const mutation of mutations) { if (mutation.type === 'attributes' && mutation.attributeName === 'class' && (mutation.target.classList.contains('fkwBLaj') || mutation.target.classList.contains('selected'))) { shouldUpdate = true; break; } } if (shouldUpdate) { updateKDownButton(); } }); // 观察整个文件列表区域 const fileList = document.querySelector('.OCR, .wp-s-pan-table__body'); if (fileList) { observer.observe(fileList, { attributes: true, attributeFilter: ['class'], subtree: true }); } } // 更新按钮状态 function updateKDownButton() { const downloadBtn = document.querySelector('.kdown-download'); if (!downloadBtn) return; const selectedItems = getSelectedItems(); const hasSelection = selectedItems.length > 0; const hasFolder = hasSelectedFolder(); // 更新按钮样式 if (hasSelection) { downloadBtn.classList.add('selected'); if (hasFolder) { downloadBtn.style.opacity = '0.5'; downloadBtn.title = '暂不支持下载文件夹'; } else { downloadBtn.style.opacity = '1'; downloadBtn.title = 'KDown下载'; } } else { downloadBtn.classList.remove('selected'); downloadBtn.style.opacity = '1'; downloadBtn.title = 'KDown下载'; } } // 修改init函数,添加导航和页面切换功能 function init() { Logger.info('开始初始化脚本'); // 清理已存在的实例 const existingContainers = document.querySelectorAll('.kdown-container'); existingContainers.forEach(container => { Logger.debug('清理已存在的容器:', container); container.remove(); }); // 清理可能存在的其他相关元素 const elementsToClean = [ '.kdown-nav-menu', '.kdown-page-container', '.kdown-parse-record-page', '.kdown-signin-page', '.kdown-about-page' ]; elementsToClean.forEach(selector => { const elements = document.querySelectorAll(selector); elements.forEach(element => { Logger.debug('清理元素:', selector); element.remove(); }); }); // 初始化菜单 initializeMenu(); // 初始化其他功能 initializeInterceptors(); observeFileSelection(); createKDownButton(); // 默认显示首页 updatePageContent('home'); Logger.success('脚本初始化完成'); } // 立即执行初始化 console.log('脚本开始运行'); init(); // 添加确认弹窗组件 function showConfirmDialog({ title, content, onConfirm, onCancel }) { const dialog = document.createElement('div'); dialog.className = 'kdown-confirm-dialog'; dialog.innerHTML = `

${title}

${content}
`; // 添加遮罩层 const overlay = document.createElement('div'); overlay.className = 'kdown-overlay'; document.body.appendChild(overlay); document.body.appendChild(dialog); // 绑定事件 const close = () => { dialog.remove(); overlay.remove(); }; dialog.querySelector('.kdown-confirm-close').onclick = () => { close(); onCancel?.(); }; dialog.querySelector('#cancelBtn').onclick = () => { close(); onCancel?.(); }; dialog.querySelector('#confirmBtn').onclick = () => { close(); onConfirm?.(); }; // ESC键关闭 document.addEventListener('keydown', function escListener(e) { if (e.key === 'Escape') { document.removeEventListener('keydown', escListener); close(); onCancel?.(); } }); return dialog; } // 添加确认弹窗样式 GM_addStyle(` .kdown-confirm-dialog { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); z-index: 10001; width: 400px; animation: dialogFadeIn 0.3s ease; } .kdown-confirm-content { display: flex; flex-direction: column; } .kdown-confirm-header { display: flex; justify-content: space-between; align-items: center; padding: 16px 24px; border-bottom: 1px solid #f0f0f0; } .kdown-confirm-header h3 { margin: 0; font-size: 16px; color: #262626; font-weight: 500; } .kdown-confirm-close { padding: 4px; background: none; border: none; cursor: pointer; color: #999; display: flex; align-items: center; justify-content: center; border-radius: 4px; transition: all 0.3s; } .kdown-confirm-close:hover { background: #f5f5f5; color: #666; } .kdown-confirm-body { padding: 24px; color: #595959; font-size: 14px; line-height: 1.5; } .kdown-confirm-footer { padding: 16px 24px; border-top: 1px solid #f0f0f0; display: flex; justify-content: flex-end; gap: 12px; } @keyframes dialogFadeIn { from { opacity: 0; transform: translate(-50%, -48%) scale(0.96); } to { opacity: 1; transform: translate(-50%, -50%) scale(1); } } `); // 删除重复的变量声明 // 修改文件列表缓存管理 class FileListManager { constructor() { this.cache = new Map(); // 路径 -> 文件列表映射 this.fsidMap = new Map(); // 路径 -> fsid映射表 this.currentPath = '/'; // 当前路径 } // 更新缓存 updateCache(path, fileList) { this.cache.set(path, fileList); // 更新当前路径的fsid映射 const fsidMapping = new Map(); fileList.forEach(file => { fsidMapping.set(file.server_filename, file.fs_id); }); this.fsidMap.set(path, fsidMapping); this.currentPath = path; Logger.info(`缓存更新 - 路径: ${path}, 文件数: ${fileList.length}`); } // 获取缓存 getCache(path) { return this.cache.get(path); } // 获取文件的fsid getFsId(path, fileName) { const fsidMapping = this.fsidMap.get(path); if (fsidMapping) { const fsid = fsidMapping.get(fileName); Logger.debug(`获取fsid - 路径: ${path}, 文件: ${fileName}, fsid: ${fsid}`); return fsid; } return null; } // 获取当前路径 getCurrentPath() { return this.currentPath; } // 设置当前路径 setCurrentPath(path) { this.currentPath = path; Logger.debug(`当前路径已更新: ${path}`); } // 清除指定路径的缓存 clearCache(path) { this.cache.delete(path); this.fsidMap.delete(path); Logger.info(`缓存已清除 - 路径: ${path}`); } // 清除所有缓存 clearAllCache() { this.cache.clear(); this.fsidMap.clear(); Logger.info('所有缓存已清除'); } } // 创建文件列表管理器实例 const fileListManager = new FileListManager(); // 添加云端配置管理 class CloudConfig { constructor() { this.config = { serverUrl: 'https://jiexi.moiu.cn', title: ['欢迎使用KDown下载助手'], ads: [] }; this.lastFetchTime = 0; this.fetchInterval = 5 * 60 * 1000; // 5分钟缓存 this.initialized = false; } async initialize() { try { Logger.info('开始初始化云端配置'); const response = await fetch('https://assets.moiu.cn/json/server.json'); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); Logger.debug('获取到的云端配置:', data); this.config = { serverUrl: data.serverUrl || this.config.serverUrl, title: data.title || this.config.title, ads: Array.isArray(data.ads) ? data.ads : [] }; this.lastFetchTime = Date.now(); this.initialized = true; Logger.success('云端配置初始化成功'); } catch (error) { Logger.error('云端配置初始化失败:', error); // 使用默认配置 this.initialized = true; } } async getConfig() { if (!this.initialized || Date.now() - this.lastFetchTime > this.fetchInterval) { await this.initialize(); } return this.config; } getServerUrl() { return this.config.serverUrl; } async getAds() { Logger.debug('开始获取广告数据'); const config = await this.getConfig(); if (!Array.isArray(config.ads)) { Logger.warning('广告数据格式不正确'); return []; } Logger.debug('返回广告数据:', config.ads); return config.ads; } async getAnnouncements() { const config = await this.getConfig(); return Array.isArray(config.title) ? config.title : []; } } // 创建全局单例 const cloudConfig = new CloudConfig(); // 添加用户积分管理 class UserCreditsManager { constructor() { this.credits = GM_getValue('user_credits', 10); // 默认给予10个积分 this.lastFetchTime = 0; this.fetchInterval = 60 * 1000; // 1分钟缓存 } async fetchCreditsFromServer() { try { const cookies = await getCookieObject(); if (!cookies || !cookies.BDUSS) { throw new Error('获取Cookie失败或BDUSS无效'); } const cookieString = Object.entries(cookies) .map(([k, v]) => `${k}=${encodeURIComponent(v)}`) .join('; '); const response = await fetch('https://api.moiu.cn/one_api.php', { method: 'POST', headers: sanitizeHeaders({ 'Content-Type': 'application/json' }), body: JSON.stringify({ action: 'points', cookie: cookieString }) }); const data = await response.json(); if (data.status === 1 && data.data && typeof data.data.points === 'number') { this.credits = data.data.points; GM_setValue('user_credits', this.credits); this.lastFetchTime = Date.now(); Logger.info('成功从服务器获取积分:', this.credits); return this.credits; } else { throw new Error(data.msg || '获取积分失败'); } } catch (error) { Logger.error('获取积分失败:', error); return this.credits; // 失败时返回本地缓存的积分 } } async getCredits() { // 如果距离上次获取时间超过缓存时间,则重新获取 if (Date.now() - this.lastFetchTime > this.fetchInterval) { await this.fetchCreditsFromServer(); } return this.credits; } useCredits(amount = 1) { if (this.credits >= amount) { this.credits -= amount; GM_setValue('user_credits', this.credits); return true; } return false; } addCredits(amount) { this.credits += amount; GM_setValue('user_credits', this.credits); } } // 创建用户积分管理实例 const creditsManager = new UserCreditsManager(); const userCredits = creditsManager.getCredits(); // 添加公告组件 class Announcement { constructor() { this.announcements = []; this.container = null; } async init() { this.announcements = await cloudConfig.getAnnouncements(); if (this.announcements.length === 0) return; // 创建公告容器 this.container = document.createElement('div'); this.container.className = 'kdown-announcement'; this.container.innerHTML = `
📢 公告 ×
`; // 添加样式 GM_addStyle(` .kdown-announcement { margin: 16px; margin-top: 80px; background: #fff; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); overflow: hidden; } .kdown-announcement-header { display: flex; align-items: center; padding: 12px 16px; background: #f5f5f5; border-bottom: 1px solid #e8e8e8; } .announcement-icon { margin-right: 8px; } .announcement-title { font-weight: 500; color: #262626; flex: 1; } .announcement-close { cursor: pointer; color: #999; font-size: 18px; padding: 4px; } .announcement-close:hover { color: #666; } .kdown-announcement-content { padding: 16px; color: #595959; line-height: 1.6; } .kdown-announcement-content a { color: #1890ff; text-decoration: none; } .kdown-announcement-content a:hover { text-decoration: underline; } .kdown-announcement-content .red { color: #ff4d4f; } .kdown-announcement-content .bold { font-weight: 500; } `); // 添加到页面 const mainContent = document.querySelector('.KPDwCE'); if (mainContent) { mainContent.parentNode.insertBefore(this.container, mainContent); } // 绑定关闭事件 const closeBtn = this.container.querySelector('.announcement-close'); if (closeBtn) { closeBtn.addEventListener('click', () => this.hide()); } // 显示公告内容 this.showAnnouncement(); } formatContent(content) { // 处理URL链接 content = content.replace(/(.*?)\|(.*?)<\/url>/g, '$2'); // 处理换行 content = content.replace(/<\/br>/g, '
'); // 处理段落 content = content.replace(/<\/p>/g, '

'); // 处理红色文字 content = content.replace(/<\/red>/g, '').replace(//g, ''); // 处理加粗 content = content.replace(//g, '').replace(/<\/b>/g, ''); return content; } showAnnouncement() { const content = this.container.querySelector('.kdown-announcement-content'); const formattedContent = this.announcements.map(announcement => this.formatContent(announcement)).join('
'); content.innerHTML = formattedContent; this.container.style.display = 'block'; } hide() { if (this.container && this.container.parentNode) { this.container.parentNode.removeChild(this.container); } } } // 添加广告组件 class AdsManager { constructor() { Logger.debug('初始化 AdsManager'); this.ads = []; this.currentAdIndex = 0; this.containers = []; } async init() { Logger.info('开始初始化广告模块'); try { this.ads = await cloudConfig.getAds(); Logger.debug('获取到广告数据:', this.ads); if (this.ads.length === 0) { Logger.warning('没有广告数据可显示'); return; } // 为每个广告创建独立容器 this.ads.forEach((ad, index) => { const container = document.createElement('div'); container.className = 'kdown-corner-ad'; container.style.bottom = `${20 + index * 90}px`; // 每个广告之间留出间距 container.innerHTML = ` `; document.body.appendChild(container); this.containers.push(container); // 绑定关闭按钮事件 const closeBtn = container.querySelector('.kdown-ad-close'); closeBtn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); container.remove(); Logger.debug('关闭广告:', index); }); }); // 添加样式 GM_addStyle(` .kdown-corner-ad { position: fixed; right: 20px; z-index: 9999; width: 240px; background: white; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.15); transition: all 0.3s ease; } .kdown-corner-ad:hover { transform: translateX(-5px); } .kdown-ad-content { position: relative; padding: 12px; } .kdown-ad-link { text-decoration: none; color: inherit; } .kdown-ad-card { padding: 8px; background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); border-radius: 6px; display: flex; flex-direction: column; gap: 8px; } .kdown-ad-card:hover { background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%); } .kdown-ad-tag { align-self: flex-start; padding: 2px 8px; background: #28a745; color: white; border-radius: 12px; font-size: 12px; line-height: 1.4; } .kdown-ad-text { margin: 0; color: #495057; font-size: 14px; line-height: 1.6; font-weight: 500; text-align: center; } .kdown-ad-close { position: absolute; top: 8px; right: 8px; width: 20px; height: 20px; border: none; background: rgba(0,0,0,0.1); color: #666; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 16px; line-height: 1; padding: 0; transition: all 0.2s; } .kdown-ad-close:hover { background: rgba(0,0,0,0.2); color: #333; } @keyframes slideIn { from { opacity: 0; transform: translateX(20px); } to { opacity: 1; transform: translateX(0); } } .kdown-corner-ad { animation: slideIn 0.3s ease; } `); Logger.success('广告模块初始化完成'); } catch (error) { Logger.error('广告模块初始化失败:', error); } } } // 主初始化函数 async function initializeApp() { Logger.info('开始初始化应用'); // 检查页面路径 const path = window.location.pathname; if (path === '/disk/main') { Logger.info('检测到新版页面,提示跳转到旧版'); showConfirmDialog({ title: '提示', content: 'KDown 目前仅支持旧版界面,是否跳转?', onConfirm: () => { window.location.replace('/disk/home?from=newversion&stayAtHome=true'); } }); return; } try { // 初始化云端配置 await cloudConfig.initialize(); // 初始化其他功能 init(); // 初始化公告和广告 const announcement = new Announcement(); await announcement.init(); const adsManager = new AdsManager(); await adsManager.init(); Logger.success('应用初始化完成'); } catch (error) { Logger.error('应用初始化失败', error); toast.error('初始化失败,请刷新页面重试'); } } // 启动应用 initializeApp(); // 添加样式 GM_addStyle(` .announcement-container { position: fixed; top: 0; left: 0; right: 0; background: #f8f9fa; padding: 10px; text-align: center; z-index: 9999; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .announcement-text { color: #333; font-size: 14px; margin: 0; } .ads-container { margin: 10px 0; padding: 10px; background: #fff; border-radius: 4px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); } .close-btn { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); cursor: pointer; padding: 5px; background: none; border: none; color: #666; } `); // 添加极验验证和签到相关代码 // 添加极验配置 const GEETEST_CONFIG = { GEETEST_ID: 'b1ccd0c65aa6a64f73f6363438f57374' }; // 加载极验脚本 function loadGeetest() { return new Promise((resolve, reject) => { if (typeof initGeetest4 !== 'undefined') { resolve(); return; } const script = document.createElement('script'); script.src = 'https://static.geetest.com/v4/gt4.js'; script.async = true; script.onerror = () => reject(new Error('加载极验脚本失败')); script.onload = () => { if (typeof initGeetest4 === 'undefined') { reject(new Error('极验脚本加载成功但初始化失败')); } else { resolve(); } }; document.head.appendChild(script); }); } class SignInManager { constructor() { Logger.debug('初始化SignInManager'); this.captcha = null; } async startCaptcha() { Logger.debug('开始初始化验证码'); try { // 首先加载极验脚本 Logger.debug('开始加载极验脚本'); await loadGeetest(); Logger.debug('极验脚本加载成功'); // 等待容器可用 let retries = 0; const maxRetries = 5; let container; while (retries < maxRetries) { container = document.getElementById('geetest-wrap'); if (container) break; await new Promise(resolve => setTimeout(resolve, 100)); retries++; Logger.debug(`等待验证码容器,重试次数: ${retries}`); } if (!container) { throw new Error('验证码容器不存在'); } // 确保容器是空的 container.innerHTML = ''; // 显示加载状态 container.innerHTML = '
正在加载验证码...
'; // 初始化验证码 return new Promise((resolve, reject) => { initGeetest4({ captchaId: GEETEST_CONFIG.GEETEST_ID, product: 'popup', language: 'zho', riskType: 'slide', onError: (error) => { Logger.error('验证码出错:', error); const statusEl = document.querySelector('.signin-status'); if (statusEl) { statusEl.innerHTML = ` 验证失败: ${error.message || '请重试'} `; } reject(error); } }, (captcha) => { Logger.debug('验证码初始化成功'); this.captcha = captcha; // 清除加载状态 container.innerHTML = ''; // 添加验证码到容器 captcha.appendTo('#geetest-wrap'); // 绑定验证回调 captcha.onSuccess(() => { const result = captcha.getValidate(); Logger.debug('验证成功,开始签到', result); this.signIn(result); }); resolve(captcha); }); }); } catch (error) { Logger.error('验证码初始化失败:', error); const statusEl = document.querySelector('.signin-status'); if (statusEl) { statusEl.innerHTML = ` 初始化失败: ${error.message} `; } throw error; } } async signIn(validateResult) { try { Logger.debug('开始签到流程', validateResult); const statusEl = document.querySelector('.signin-status'); if (!validateResult) { throw new Error('验证结果无效'); } // 获取Cookie对象 const cookieObject = await getCookieObject(); if (!cookieObject || !cookieObject.BDUSS) { throw new Error('未获取到BDUSS,请先登录'); } // 发送签到请求 const response = await fetch('https://api.moiu.cn/one_api.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `action=sign&cookie=BDUSS=${cookieObject.BDUSS}&${new URLSearchParams(validateResult).toString()}` }); const result = await response.json(); Logger.debug('签到结果:', result); if (result.status === 1) { if (statusEl) { statusEl.innerHTML = ` ${result.msg} `; } // 重置验证码 if (this.captcha) { this.captcha.reset(); } } else { throw new Error(result.msg || '签到失败'); } } catch (error) { Logger.error('签到失败:', error); const statusEl = document.querySelector('.signin-status'); if (statusEl) { statusEl.innerHTML = ` 签到失败: ${error.message} `; } // 重置验证码 if (this.captcha) { this.captcha.reset(); } } } } // 添加验证码加载样式 GM_addStyle(` .captcha-loading { text-align: center; padding: 20px; color: #666; font-size: 14px; display: flex; align-items: center; justify-content: center; gap: 8px; } .captcha-loading:before { content: ''; width: 16px; height: 16px; border: 2px solid #f3f3f3; border-top: 2px solid #1890ff; border-radius: 50%; animation: spin 1s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } `); // 修改签到按钮创建函数 function createSignInPage() { Logger.debug('创建签到页面'); const signInPage = document.createElement('div'); signInPage.className = 'kdown-signin-page'; // 创建签到页面内容 signInPage.innerHTML = ` `; // 添加样式 GM_addStyle(` .kdown-signin-page { padding: 20px; } .kdown-signin-container { max-width: 400px; margin: 0 auto; background: #fff; border-radius: 12px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); padding: 24px; } .kdown-signin-header { text-align: center; margin-bottom: 24px; } .kdown-signin-header h3 { margin: 0; font-size: 20px; color: #262626; } .signin-desc { margin: 8px 0 0; color: #666; font-size: 14px; } .kdown-signin-content { display: flex; flex-direction: column; align-items: center; gap: 16px; } .geetest-wrap { width: 100%; min-height: 150px; } .signin-button { padding: 8px 24px; font-size: 16px; color: #fff; background: #4e6ef2; border: none; border-radius: 4px; cursor: pointer; transition: all 0.3s ease; } .signin-button:hover { background: #4058d9; } .signin-button:disabled { background: #b4b4b4; cursor: not-allowed; } `); // 获取元素 const signInButton = signInPage.querySelector('.signin-button'); const geetestWrap = signInPage.querySelector('#geetest-wrap'); let validateResult = null; // 初始化验证码 const initCaptcha = async () => { try { // 加载极验脚本 await loadGeetest(); return new Promise((resolve, reject) => { initGeetest4({ captchaId: GEETEST_CONFIG.GEETEST_ID, product: 'popup', language: 'zho', riskType: 'slide', onError: (error) => { Logger.error('验证码出错:', error); toast.show('验证码加载失败,请刷新重试', 'error'); reject(error); } }, (captcha) => { Logger.debug('验证码初始化成功'); // 添加验证码到容器 captcha.appendTo('#geetest-wrap'); // 绑定验证成功事件 captcha.onSuccess(() => { validateResult = captcha.getValidate(); Logger.debug('验证通过', validateResult); toast.show('验证通过,请点击签到按钮完成签到', 'success'); signInButton.style.display = 'block'; }); // 绑定关闭事件 captcha.onClose(() => { if (!validateResult) { toast.show('请完成验证后再签到', 'warning'); } }); resolve(captcha); }); }); } catch (error) { Logger.error('初始化验证码失败:', error); toast.show('初始化验证码失败,请刷新重试', 'error'); throw error; } }; // 添加签到按钮点击事件 signInButton.addEventListener('click', async () => { if (!validateResult) { toast.show('请先完成验证', 'warning'); return; } try { signInButton.disabled = true; signInButton.textContent = '签到中...'; // 获取cookie const cookie = await gmCookie('.baidu.com'); if (!cookie || !cookie.BDUSS) { throw new Error('无法获取BDUSS,请确保已登录百度网盘'); } // 构建签到请求数据 const signData = { action: 'sign', cookie: `BDUSS=${cookie.BDUSS.value}`, ...validateResult }; Logger.debug('发送签到请求', signData); // 发送签到请求 const response = await fetch('https://api.moiu.cn/one_api.php', { method: 'POST', headers: { 'Content-Type': 'application/json', 'User-Agent': navigator.userAgent }, body: JSON.stringify(signData) }); if (!response.ok) { throw new Error(`HTTP错误: ${response.status}`); } const result = await response.json(); Logger.debug('签到响应', result); if (result.status === 1) { toast.show(result.msg, 'success'); signInButton.textContent = '签到成功'; // 更新用户积分 if (result.data && result.data.total_points) { creditsManager.addCredits(result.data.points_added); } } else { throw new Error(result.msg || '签到失败'); } } catch (error) { Logger.error('签到失败', error); toast.show(error.message || '签到失败,请稍后重试', 'error'); signInButton.textContent = '签到失败'; } finally { // 3秒后重置按钮状态 setTimeout(() => { signInButton.disabled = false; signInButton.textContent = '立即签到'; // 重置验证结果 validateResult = null; signInButton.style.display = 'none'; // 重新初始化验证码 initCaptcha().catch(error => { Logger.error('重新初始化验证码失败:', error); }); }, 3000); } }); // 初始化验证码 initCaptcha().catch(error => { Logger.error('初始化验证码失败:', error); }); Logger.debug('签到页面创建完成'); return signInPage; } // 添加缺失的函数定义 function initButtons() { Logger.debug('开始初始化按钮'); // 查找按钮插入位置 const buttonContainer = document.querySelector('.QDDOQB'); if (!buttonContainer) { Logger.error('未找到按钮容器'); return; } // 创建并插入 KDown 按钮 const kdownButton = createKDownButton(); buttonContainer.insertBefore(kdownButton, buttonContainer.firstChild); // 初始化按钮状态 updateKDownButton(); // 监听文件选择变化 observeFileSelection(); Logger.success('按钮初始化完成'); } // 添加全局错误处理 window.addEventListener('error', function(event) { // 忽略特定错误 if (event.message.includes('initButtons is not defined')) { event.preventDefault(); return; } // 其他错误正常处理 Logger.error('全局错误', event); }); // 添加关于页面生成函数 async function generateAboutPage() { try { Logger.info('开始获取关于页面信息'); // 获取Cookie对象 const cookieObject = await getCookieObject(); if (!cookieObject || !cookieObject.BDUSS) { throw new Error('未获取到BDUSS,请先登录'); } // 发送请求获取关于信息 const response = await fetch('https://api.moiu.cn/one_api.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `action=about&cookie=BDUSS=${cookieObject.BDUSS}` }); const result = await response.json(); if (result.status !== 1) { throw new Error(result.msg || '获取关于信息失败'); } const aboutConfig = result.data; Logger.success('获取关于信息成功', aboutConfig); // 判断版本是否最新 const currentVersion = GM_info.script.version; const isLatestVersion = currentVersion === aboutConfig.version; const versionStatus = isLatestVersion ? '已是最新版本' : '发现新版本'; return `

KDown 网盘助手

当前版本: ${currentVersion}
最新版本: ${aboutConfig.version}
${versionStatus}
${aboutConfig.description || '一个简单好用的百度网盘增强脚本'}
`; } catch (error) { Logger.error('生成关于页面失败', error); return `

加载失败: ${error.message}

`; } } // 更新关于页面样式 GM_addStyle(` .kdown-about-container { max-width: 600px; margin: 20px auto; padding: 24px; background: #fff; border-radius: 12px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .kdown-about-header { text-align: center; margin-bottom: 24px; } .kdown-about-logo { width: 80px; height: 80px; margin-bottom: 12px; border-radius: 16px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); } .kdown-version-info { color: #666; font-size: 14px; margin: 16px 0; line-height: 1.6; } .version-status { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 12px; margin-top: 8px; } .version-status.latest { background: #f6ffed; color: #52c41a; border: 1px solid #b7eb8f; } .version-status.outdated { background: #fff7e6; color: #fa8c16; border: 1px solid #ffd591; } .kdown-update-url { margin-top: 16px; } .update-link { display: inline-flex; align-items: center; gap: 8px; padding: 8px 16px; background: #1890ff; color: white; text-decoration: none; border-radius: 6px; transition: all 0.3s; } .update-link:hover { background: #40a9ff; transform: translateY(-1px); } .update-icon { width: 16px; height: 16px; background-image: url('data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIxLjEgMTQuN0wxNy45IDExbC0xLjQgMS40IDIuNCAyLjRIMTJ2MmgxMS4zTDIxIDIwLjJsMS40LTEuNC0xLjMtNC4xek0zLjkgMTJoNi4xVjEwSDIuN2wyLjQtMi40TDMuNyA2LjNsLTMuMiAzLjcgMS4zIDQuMUwzLjIgMTZsLjctMi4yVjEyeiIgZmlsbD0iI2ZmZiIvPjwvc3ZnPg=='); background-size: contain; background-repeat: no-repeat; } .kdown-about-desc { color: #595959; line-height: 1.6; text-align: center; padding: 0 20px; } .kdown-about-error { text-align: center; padding: 48px 24px; background: #fff; border-radius: 12px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); margin: 20px auto; max-width: 400px; } .kdown-about-error p { color: #595959; margin: 16px 0; } .kdown-retry-btn { padding: 8px 16px; background: #1890ff; color: white; border: none; border-radius: 4px; cursor: pointer; transition: all 0.3s; } .kdown-retry-btn:hover { background: #40a9ff; } `); // 云端加速处理函数 async function handleCloudAcceleration(shareUrl, sharePassword) { try { Logger.info('开始云端加速处理', { shareUrl }); // 从分享链接中提取 surl const surlMatch = shareUrl.match(/\/s\/([^/?]+)/); if (!surlMatch) { throw new Error('无效的分享链接'); } const surl = surlMatch[1]; // 获取 Cookie const cookies = await getCookieObject(); if (!cookies || !cookies.BDUSS) { throw new Error('获取Cookie失败或BDUSS无效'); } // 处理密码 - 确保是4位字符串 let pwd = '0000'; // 默认密码 if (sharePassword) { if (sharePassword.length === 4) { pwd = sharePassword; } else { Logger.warning('分享密码长度不是4位,使用默认密码'); } } // 构建Cookie字符串 const cookieString = Object.entries(cookies) .filter(([k, v]) => k && v) // 过滤掉空值 .map(([k, v]) => `${k}=${v}`) .join('; '); Logger.info('使用的Cookie:', cookieString); // 获取文件列表 const listResponse = await fetch(API_URL, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Cookie': cookieString // 在headers中也设置Cookie }, body: new URLSearchParams({ action: 'list', Cookie: cookieString, surl: surl, pwd: pwd, dir: '/' }) }); const listData = await listResponse.json(); if (listData.status !== 1 || !listData.data) { throw new Error(listData.msg || '获取文件列表失败'); } const { randsk, shareid, uk } = listData.data.info; // 过滤出非文件夹的文件 const files = listData.data.list.filter(file => file.isdir !== "1"); // 获取每个文件的下载链接 const downloadPromises = files.map(async file => { const params = { action: 'getDownloadUrl', Cookie: cookieString, // 使用相同的Cookie字符串 fsidlist: file.fs_id, sekey: randsk, shareid: shareid, uk: uk, surl: surl, size: file.size }; const response = await fetch(API_URL, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Cookie': cookieString // 在headers中也设置Cookie }, body: new URLSearchParams(params) }); const result = await response.json(); Logger.debug('获取下载链接返回结果:', result); if (result.status !== 1 || !result.data) { throw new Error(`获取文件 ${file.server_filename} 的下载链接失败:${result.msg}`); } // 保存完整的文件信息 const data = result.data; return { filename: data.filename || file.server_filename, filesize: data.filesize || file.size, dlink: data.dlink || '', urls: data.urls || [], ua: data.ua || navigator.userAgent, filemd5: data.filemd5 || file.md5 || '', fs_id: file.fs_id }; }); const downloadResults = await Promise.all(downloadPromises); Logger.debug('所有下载链接获取结果:', downloadResults); // 保存解析记录 const recordManager = new ParseRecordManager(); downloadResults.forEach(data => { const record = { fileName: data.filename, fileSize: String(data.filesize || '0'), dlink: data.dlink, fsId: data.fs_id, download_ua: data.ua || '', md5: data.filemd5 || '', timestamp: Date.now(), server_filename: data.filename }; Logger.debug('添加解析记录:', record); recordManager.addRecord(record); }); return downloadResults; } catch (error) { Logger.error('云端加速处理失败', error); toast.show(error.message, 'error'); return null; } } // 修改 createShareLink 函数,添加云端加速支持 async function createShareLink(files) { try { const shareInfo = await shareLinkManager.getShareLink(files.map(f => f.fs_id)); if (!shareInfo) { throw new Error('创建分享链接失败'); } Logger.info('分享链接创建成功', shareInfo); return shareInfo; } catch (error) { Logger.error('处理分享链接失败', error); if (error && error.message) { toast.show(error.message, 'error'); } else { toast.show('处理分享链接失败', 'error'); } return null; } } // 修改updatePageContent函数 function updatePageContent(page) { Logger.debug('开始切换页面:', page); const mainContent = document.querySelector('.KPDwCE'); const container = document.querySelector('.kdown-page-container'); if (!container) { Logger.error('页面容器不存在'); return; } // 清除旧内容,但保留导航菜单 const navMenu = container.querySelector('.kdown-nav-menu'); container.innerHTML = ''; // 完全清空 if (navMenu) { container.appendChild(navMenu); // 重新添加导航菜单 } // 根据页面类型创建新内容 switch(page) { case 'home': if (mainContent) mainContent.style.display = 'block'; break; case 'records': const recordPage = createParseRecordPage(); container.appendChild(recordPage); if (mainContent) mainContent.style.display = 'none'; // 确保记录列表被更新 if (typeof updateRecordList === 'function') { updateRecordList(); } break; case 'signin': const signInPage = createSignInPage(); container.appendChild(signInPage); if (mainContent) mainContent.style.display = 'none'; break; case 'about': const aboutPage = document.createElement('div'); aboutPage.className = 'kdown-about-page'; container.appendChild(aboutPage); // 异步加载关于页面内容 generateAboutPage().then(content => { aboutPage.innerHTML = content; }).catch(error => { Logger.error('加载关于页面失败:', error); aboutPage.innerHTML = '
加载失败,请稍后重试
'; }); if (mainContent) mainContent.style.display = 'none'; break; } Logger.debug('页面切换完成:', page); } // 添加自动重新创建按钮的逻辑 const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'childList' || mutation.type === 'subtree') { const toolbar = document.querySelector('.QDDOQB'); const downloadBtn = document.querySelector('.kdown-download'); if (toolbar && !downloadBtn) { Logger.info('检测到工具栏变化,重新创建KDown按钮'); createKDownButton(); } } }); }); // 开始观察工具栏变化 const toolbarContainer = document.querySelector('.nd-main-layout, .frame-content'); if (toolbarContainer) { observer.observe(toolbarContainer, { childList: true, subtree: true }); Logger.info('开始监听工具栏变化'); } function AddElement() { if (document.getElementById("KDown") === null) { const toolbar = document.querySelector("div.wp-s-agile-tool-bar__header"); if (toolbar) { // 添加样式 GM_addStyle([ ".kdown-credits {", " display: flex;", " align-items: center;", " margin-left: auto;", " margin-right: 16px;", " padding: 6px 12px;", " border-radius: 4px;", " background: rgba(6, 167, 255, 0.1);", " color: #06a7ff;", " font-weight: 500;", " font-size: 14px;", " cursor: default;", " transition: all 0.3s ease;", "}", ".kdown-credits:hover {", " background: rgba(6, 167, 255, 0.15);", "}", ".kdown-credits-icon {", " margin-right: 6px;", " font-size: 16px;", "}" ].join("\n")); // 创建积分显示元素 const creditsDisplay = document.createElement("div"); creditsDisplay.className = "kdown-credits"; creditsDisplay.innerHTML = '💰加载中...'; const newButton = document.createElement("button"); newButton.id = "KDown"; newButton.className = "u-button nd-file-list-toolbar-action-item u-button--primary"; newButton.style.marginRight = "8px"; newButton.innerText = "KDown"; const statusButton = document.createElement("button"); statusButton.id = "KDownStatus"; statusButton.className = "u-button nd-file-list-toolbar-action-item u-button--primary"; statusButton.style.marginRight = "8px"; statusButton.innerText = "KDown Status"; // 先添加积分显示 toolbar.appendChild(creditsDisplay); // 再添加其他按钮 toolbar.prepend(statusButton); toolbar.prepend(newButton); newButton.addEventListener("click", handleKDownClick); statusButton.addEventListener("click", handleKDownStatusClick); // 定期更新积分显示 async function updateCreditsDisplay() { try { const credits = await creditsManager.getCredits(); const creditsValue = document.getElementById("KDownCreditsValue"); if (creditsValue) { creditsValue.textContent = credits + " 积分"; } } catch (error) { Logger.error('更新积分显示失败:', error); } } // 初始更新 updateCreditsDisplay(); // 每60秒更新一次积分显示 setInterval(updateCreditsDisplay, 60000); // 监听积分变化 const originalUseCredits = creditsManager.useCredits; creditsManager.useCredits = function(amount) { const result = originalUseCredits.call(this, amount); if (result) { updateCreditsDisplay(); } return result; }; const originalAddCredits = creditsManager.addCredits; creditsManager.addCredits = function(amount) { originalAddCredits.call(this, amount); updateCreditsDisplay(); }; } else { setTimeout(AddElement, 100); } } else { Logger.info('KDown button already added.'); } } })();