// ==UserScript== // @name 网盘下载链接自动捕获助手 Pro // @namespace https://workbuddy.local/download-accelerator // @version 2.0.0 // @description 自动解析夸克/UC/百度网盘分享页面文件列表,批量获取直链下载地址+Cookie,一键导出到下载加速器 // @author DownloadAccelerator // @match *://pan.quark.cn/* // @match *://drive.uc.cn/* // @match *://pan.baidu.com/* // @match *://yun.baidu.com/* // @icon data:image/svg+xml, // @grant GM_setClipboard // @grant GM_notification // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @connect pan.quark.cn // @connect drive.uc.cn // @connect pan.baidu.com // @connect d.pcs.baidu.com // @connect * // @run-at document-start // @license MIT // ==/UserScript== (function () { 'use strict'; // ============================================================ // 全局配置 // ============================================================ const PANEL_ID = 'dl-accel-panel'; const MAX_LINKS = 200; const AUTO_CAPTURE = true; const AUTO_COPY = false; const SITE = detectSite(); let capturedLinks = []; let fileList = []; // 解析到的文件列表 let isResolving = false; // 是否正在解析 let shareInfo = null; // 当前分享页信息 let autoCopyEnabled = GM_getValue('autoCopy', AUTO_COPY); // ============================================================ // 站点检测 // ============================================================ function detectSite() { const host = location.hostname; if (host.includes('quark')) { return { name: '夸克网盘', key: 'quark', host: 'https://pan.quark.cn' }; } if (host.includes('uc.cn')) { return { name: 'UC网盘', key: 'uc', host: 'https://drive.uc.cn' }; } if (host.includes('baidu')) { return { name: '百度网盘', key: 'baidu', host: 'https://pan.baidu.com' }; } return { name: '未知网盘', key: 'unknown', host: '' }; } // ============================================================ // 分享页面检测 // ============================================================ function detectSharePage() { const path = location.pathname; const match = path.match(/\/s\/([a-zA-Z0-9]+)/); if (match) { return { shareId: match[1], isShare: true }; } // 百度网盘 /share/init?surl=xxx const params = new URLSearchParams(location.search); const surl = params.get('surl'); if (surl) { return { shareId: surl, isShare: true }; } return { shareId: null, isShare: false }; } // ============================================================ // [核心] 网络请求拦截 - fetch // ============================================================ const originalFetch = window.fetch; window.fetch = function (...args) { const url = typeof args[0] === 'string' ? args[0] : (args[0]?.url || ''); const options = args[1] || {}; const promise = originalFetch.apply(this, args); // 异步分析响应 if (AUTO_CAPTURE) { promise.then(response => { const cloned = response.clone(); cloned.text().then(text => { analyzeResponse(url, options, text, cloned.status, cloned.headers); }).catch(() => {}); }).catch(() => {}); } return promise; }; // ============================================================ // [核心] 网络请求拦截 - XMLHttpRequest // ============================================================ const OriginalXHR = window.XMLHttpRequest; const xhrOpen = OriginalXHR.prototype.open; const xhrSend = OriginalXHR.prototype.send; OriginalXHR.prototype.open = function (method, url, ...rest) { this._dl_url = url; this._dl_method = method; return xhrOpen.call(this, method, url, ...rest); }; OriginalXHR.prototype.send = function (body) { const self = this; this.addEventListener('load', function () { if (AUTO_CAPTURE) { try { analyzeResponse(self._dl_url, {}, self.responseText, self.status, null); } catch (e) {} } }); return xhrSend.call(this, body); }; // ============================================================ // [核心] 响应分析引擎 - 自动识别各种API响应格式 // ============================================================ function analyzeResponse(url, options, responseText, status, headers) { if (!url || status !== 200) return; let data = null; try { data = JSON.parse(responseText); } catch (e) { return; // 非JSON响应跳过 } // ----- 1. 检测文件列表响应 ----- const fileItems = extractFileList(url, data); if (fileItems.length > 0) { mergeFileList(fileItems); updatePanel(); showToast(`检测到 ${fileItems.length} 个文件`); } // ----- 2. 检测下载链接响应 ----- const dlLinks = extractDownloadLinks(url, data, options); for (const dl of dlLinks) { addCapturedLink(dl.url, dl.fileName, dl.fileSize, dl.fid); } // ----- 3. 检测百度网盘 yunData ----- if (SITE.key === 'baidu' && !shareInfo) { extractBaiduYunData(); } } // ============================================================ // 文件列表提取 - 支持多平台响应格式 // ============================================================ function extractFileList(url, data) { const items = []; if (!data || typeof data !== 'object') return items; // --- 夸克/UC 网盘文件列表 --- // API: /1/clouddrive/share/sharepage/detail if (url.includes('sharepage/detail') || url.includes('share/detail')) { const list = data.data?.list || data.list || []; for (const f of list) { items.push({ fid: f.fid || f.file_id || '', fileName: f.file_name || f.filename || f.name || '', fileSize: f.size || f.file_size || 0, dir: f.dir || f.is_dir || f.category === 'folder', pdirFid: f.pdir_fid || f.parent_id || '', category: f.category || f.obj_category || 'file', raw: f }); } } // --- 百度网盘文件列表 --- // API: /share/wxlist 或 /api/list if (url.includes('wxlist') || url.includes('/api/list') || url.includes('share/list')) { const list = data.data?.list || data.list || data.records || []; for (const f of list) { items.push({ fid: f.fs_id || f.fsId || '', fileName: f.server_filename || f.filename || f.name || '', fileSize: f.size || 0, dir: f.isdir === 1 || f.isdir === true, pdirFid: f.parent_path || f.path || '', category: f.category || 0, raw: f }); } } // --- 通用: 嵌套的 list 字段 --- if (items.length === 0) { const possibleList = data.data?.list || data.data?.items || data.data?.files || data.list || []; if (Array.isArray(possibleList) && possibleList.length > 0) { for (const f of possibleList) { const name = f.file_name || f.filename || f.name || f.server_filename; if (name && (f.fid || f.file_id || f.fs_id)) { items.push({ fid: f.fid || f.file_id || f.fs_id || '', fileName: name, fileSize: f.size || f.file_size || 0, dir: f.is_dir || f.isdir === 1 || f.category === 'folder', pdirFid: f.pdir_fid || f.parent_id || f.path || '', category: f.category || '', raw: f }); } } } } return items; } // ============================================================ // 下载链接提取 - 支持多平台响应格式 // ============================================================ function extractDownloadLinks(url, data, options) { const links = []; if (!data || typeof data !== 'object') return links; // --- 夸克/UC 下载链接 --- // API: /1/clouddrive/file/download/v2/detail if (url.includes('file/download') || url.includes('download/v2/detail') || url.includes('download/info')) { const dlList = data.data || []; const arr = Array.isArray(dlList) ? dlList : [dlList]; for (const item of arr) { const dlUrl = item.download_url || item.downloadUrl || item.url; if (dlUrl) { links.push({ url: dlUrl, fileName: item.file_name || item.filename || '', fileSize: item.size || 0, fid: item.fid || item.file_id || '' }); } } } // --- 百度网盘下载链接 --- // API: /api/download 或 /share/download if (url.includes('/api/download') || url.includes('/share/download') || url.includes('dlink')) { const dlList = data.dlink || data.data?.dlink || []; const arr = Array.isArray(dlList) ? dlList : [dlList]; for (const item of arr) { const dlUrl = item.dlink || item.download_url || item.url; if (dlUrl) { links.push({ url: dlUrl, fileName: item.server_filename || item.filename || '', fileSize: item.size || 0, fid: item.fs_id || '' }); } } } // --- 通用: 任何包含 download_url 的响应 --- if (links.length === 0) { const searchKeys = ['download_url', 'downloadUrl', 'dlink', 'direct_url', 'directUrl']; function deepSearch(obj, depth) { if (depth > 3 || !obj || typeof obj !== 'object') return; for (const key of Object.keys(obj)) { if (searchKeys.includes(key) && typeof obj[key] === 'string' && obj[key].startsWith('http')) { links.push({ url: obj[key], fileName: obj.file_name || obj.filename || obj.server_filename || obj.name || '', fileSize: obj.size || obj.file_size || 0, fid: obj.fid || obj.file_id || obj.fs_id || '' }); } else if (typeof obj[key] === 'object') { deepSearch(obj[key], depth + 1); } } } deepSearch(data, 0); } return links; } // ============================================================ // 百度网盘 yunData 提取 // ============================================================ function extractBaiduYunData() { if (SITE.key !== 'baidu') return; if (typeof window.yunData === 'undefined') { // 尝试从页面 script 中提取 setTimeout(extractBaiduYunData, 2000); return; } try { const yun = window.yunData; if (yun.SIGN &&yun.FS_ID) { shareInfo = { sign: yun.SIGN, bdstoken: yun.MYBDSTOKEN || yun.bdstoken, shareId: yun.SHARE_ID || '', uk: yun.SHARE_UK || yun.uk || '', fsIds: Array.isArray(yun.FS_ID) ? yun.FS_ID : [yun.FS_ID] }; } } catch (e) {} } // ============================================================ // [平台API] 夸克/UC - 获取文件列表 // ============================================================ async function quarkGetFileList(shareId, pdirFid, page) { const baseUrl = SITE.host; const apiUrl = `${baseUrl}/1/clouddrive/share/sharepage/detail?pr=ucpro&fr=pc`; try { // 先获取 share token const tokenResp = await fetch(`${baseUrl}/1/clouddrive/share/sharepage/token?pr=ucpro&fr=pc`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ share_id: shareId }) }); const tokenData = await tokenResp.json(); // 获取文件列表 const listResp = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ share_id: shareId, pdir_fid: pdirFid || '0', force: 0, _page: page || 1, _size: 50, _fetch_total: 1, _sort: 'file_type:asc,updated_at:desc' }) }); const listData = await listResp.json(); if (listData.code === 0 || listData.code === '0') { return listData.data?.list || []; } return []; } catch (e) { console.warn('[捕获助手] 获取文件列表失败:', e); return []; } } // ============================================================ // [平台API] 夸克/UC - 获取下载链接 // ============================================================ async function quarkGetDownloadLink(fids) { const baseUrl = SITE.host; const apiUrl = `${baseUrl}/1/clouddrive/file/download/v2/detail?pr=ucpro&fr=pc`; try { const resp = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ fid_list: fids }) }); const data = await resp.json(); if (data.code === 0 || data.code === '0') { return data.data || []; } return []; } catch (e) { console.warn('[捕获助手] 获取下载链接失败:', e); return []; } } // ============================================================ // [平台API] 百度网盘 - 获取下载链接 // ============================================================ async function baiduGetDownloadLink(fsIds) { const bdstoken = window.yunData?.MYBDSTOKEN || window.yunData?.bdstoken || ''; const apiUrl = `/api/download?clienttype=0&app_id=250528&web=1&bdstoken=${bdstoken}`; try { const params = new URLSearchParams(); params.append('type', 'dlink'); for (const id of fsIds) { params.append('fidlist', `[${id}]`); } const resp = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `fidlist=[${fsIds.join(',')}]&type=dlink` }); const data = await resp.json(); if (data.errno === 0) { return data.dlink || []; } return []; } catch (e) { console.warn('[捕获助手] 获取百度下载链接失败:', e); return []; } } // ============================================================ // [核心] 自动解析当前分享页 - 获取所有文件下载链接 // ============================================================ async function autoResolveSharePage() { if (isResolving) { showToast('正在解析中,请稍候...'); return; } const share = detectSharePage(); if (!share.isShare) { showToast('当前不是分享页面'); return; } isResolving = true; updateResolveButton(true); showToast('开始自动解析分享页面...'); try { if (SITE.key === 'quark' || SITE.key === 'uc') { await resolveQuarkShare(share.shareId); } else if (SITE.key === 'baidu') { await resolveBaiduShare(); } } catch (e) { showToast('解析失败: ' + e.message); console.error('[捕获助手] 解析失败:', e); } finally { isResolving = false; updateResolveButton(false); } } // 夸克/UC 分享页解析 async function resolveQuarkShare(shareId) { // 获取根目录文件列表 const files = await quarkGetFileList(shareId, '0', 1); if (files.length === 0) { showToast('未获取到文件,请确保已保存到网盘或已通过密码验证'); return; } // 过滤出文件(排除文件夹) const fileOnly = files.filter(f => !f.dir && !f.file_type?.includes('folder')); const folders = files.filter(f => f.dir || f.file_type?.includes('folder')); showToast(`发现 ${files.length} 个项目(${fileOnly.length} 文件, ${folders.length} 文件夹),正在获取下载链接...`); // 批量获取文件下载链接(每批最多10个) const batchSize = 10; for (let i = 0; i < fileOnly.length; i += batchSize) { const batch = fileOnly.slice(i, i + batchSize); const fids = batch.map(f => f.fid); const dlData = await quarkGetDownloadLink(fids); for (const dl of dlData) { addCapturedLink( dl.download_url, dl.file_name || batch.find(f => f.fid === dl.fid)?.file_name || '', dl.size || 0, dl.fid ); } updatePanel(); // 小延迟避免请求过快 if (i + batchSize < fileOnly.length) { await sleep(500); } } showToast(`解析完成!共获取 ${capturedLinks.length} 个下载链接`); } // 百度网盘分享页解析 async function resolveBaiduShare() { // 等待 yunData 加载 if (!window.yunData) { showToast('页面数据未加载完成,请稍后再试'); return; } const yun = window.yunData; const fsIds = Array.isArray(yun.FS_ID) ? yun.FS_ID : [yun.FS_ID].filter(Boolean); if (fsIds.length === 0) { showToast('未找到文件,请确保已保存到网盘'); return; } showToast(`发现 ${fsIds.length} 个文件,正在获取下载链接...`); // 批量获取下载链接 const batchSize = 5; for (let i = 0; i < fsIds.length; i += batchSize) { const batch = fsIds.slice(i, i + batchSize); const dlData = await baiduGetDownloadLink(batch); for (const dl of dlData) { addCapturedLink( dl.dlink, dl.server_filename || dl.filename || '', dl.size || 0, dl.fs_id ); } updatePanel(); if (i + batchSize < fsIds.length) { await sleep(800); } } showToast(`解析完成!共获取 ${capturedLinks.length} 个下载链接`); } // ============================================================ // 链接管理 // ============================================================ function addCapturedLink(url, fileName, fileSize, fid) { if (!url || url === '') return; // 去重 const exists = capturedLinks.find(l => l.url === url || (fid && l.fid === fid)); if (exists) { // 更新信息 if (fileName && !exists.fileName) exists.fileName = fileName; if (fileSize && !exists.fileSize) exists.fileSize = fileSize; return; } capturedLinks.unshift({ url: url, fileName: fileName || '', fileSize: fileSize || 0, fid: fid || '', cookies: document.cookie, userAgent: navigator.userAgent, referer: location.href, source: SITE.name, time: new Date().toLocaleString('zh-CN') }); if (capturedLinks.length > MAX_LINKS) capturedLinks.pop(); saveLinks(); showToast(`捕获: ${fileName || '下载链接'}`); if (autoCopyEnabled) { copyToClipboard(url); } } function mergeFileList(items) { for (const item of items) { if (!item.dir) { const exists = fileList.find(f => f.fid === item.fid); if (!exists) { fileList.push(item); } } } } function saveLinks() { try { GM_setValue('capturedLinks', JSON.stringify(capturedLinks)); } catch (e) { try { sessionStorage.setItem('dl_links', JSON.stringify(capturedLinks)); } catch(e) {} } } function loadLinks() { try { const data = GM_getValue('capturedLinks', null); if (data) { capturedLinks = JSON.parse(data) || []; } else { const ss = sessionStorage.getItem('dl_links'); if (ss) capturedLinks = JSON.parse(ss) || []; } } catch (e) { capturedLinks = []; } } // ============================================================ // UI - 浮动面板 // ============================================================ function createPanel() { if (document.getElementById(PANEL_ID)) return; const panel = document.createElement('div'); panel.id = PANEL_ID; panel.innerHTML = `