// ==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]) => `
`).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 = `
| 文件名 |
大小 |
解析时间 |
操作 |
${records.length === 0 ? `
|
|
` : records.map(record => `
|
${record.fileName}
|
${record.fileSize || '-'} |
${new Date(record.timestamp).toLocaleString()} |
|
`).join('')}
${totalPages > 1 ? `
` : ''}
`;
// 绑定事件处理
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.map(file => `
|
${file.name || '未知文件名'}
|
${file.isFolder ? '文件夹' : '文件'} |
${file.size || '-'} |
${file.time || '-'} |
`).join('')}
`;
// 添加样式
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 = `
`;
// 添加遮罩层
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 `
${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.');
}
}
})();