// ==UserScript== // @name 百度网盘网页端增强 // @namespace https://scriptcat.org/users/pwhscr // @version 1.0.0 // @description 百度网盘网页端实用增强:视频倍速解锁(最高 3 倍)、文件清单导出 CSV、批量选择助手、文件夹容量统计、自动清理客户端引导弹窗。 // @author pwhscr // @license MIT // @homepageURL https://userscripts-toolkit.app.workbuddy.host/ // @supportURL https://userscripts-toolkit.app.workbuddy.host/ // @match https://pan.baidu.com/* // @match https://yun.baidu.com/* // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @run-at document-idle // @noframes // ==/UserScript== /* * 关于「网页不限速下载」——说清楚为什么没有这个功能 * * 1. 限速的执行点在百度服务端。非会员账号请求下载链接时,服务端下发的 * 本身就是低速通道(并附带签名与时效)。前端脚本能改的只有页面,改不了 * 服务端给你的带宽。所以「油猴脚本解锁限速」在原理上不成立。 * 2. 历史上那些「网盘直链/不限速」脚本,实际是把你账号的登录凭据交给第三方 * 解析站(很多还共用会员账号),由那边中转。风险是实打实的: * 账号可能被判定异常甚至封禁、文件列表与内容经过陌生服务器、解析站随时关停。 * 这种方案我不做。 * 3. 真正想要速度,正道只有:开通会员、或用官方客户端的会员加速通道。 * * 所以这个脚本做的是另一类事——不改变任何服务端行为,只把网页端本来就有的 * 能力变得更好用:播放器倍速解锁(这是纯前端、且确实被网页端人为限制的)、 * 清单导出、批量选择、容量统计。 */ (function () { 'use strict'; const CLIENT_POPUP_RE = /客户端|百度网盘APP|扫码登录|下载APP/; const RATE_KEY = 'bdpan_rate'; const RATE_PRESETS = [0.75, 1, 1.25, 1.5, 2, 2.5, 3]; function readBool(key, def) { const v = GM_getValue(key, def); if (v === false || v === 'false' || v === 0 || v === '0') return false; if (v === true || v === 'true' || v === 1 || v === '1') return true; return !!def; } const state = { rate: parseFloat(GM_getValue(RATE_KEY, '2')) || 2, applyRate: readBool('apply_rate', true), selection: new Set(), }; function visible(el) { if (!el || el.nodeType !== 1) return false; const r = el.getBoundingClientRect(); return r.width > 4 && r.height > 4; } function sleep(ms) { return new Promise((r) => setTimeout(r, ms)); } // ---------- 1. 视频倍速解锁 ---------- let rateWatch = null; function applyRateTo(video) { if (!video || !state.applyRate) return; const want = state.rate; if (Math.abs((video.playbackRate || 1) - want) < 0.01) return; video.playbackRate = want; // 网页端播放器可能在 setRate 时把值改回 1.5,这里再压一次 setTimeout(() => { if (state.applyRate && Math.abs(video.playbackRate - want) >= 0.01) { video.playbackRate = want; } }, 120); } function scanVideos() { const videos = [...document.querySelectorAll('video')].filter(visible); videos.forEach((v) => { if (!v.dataset.bdRateHooked) { v.dataset.bdRateHooked = '1'; // 播放器主动改速率时立即纠正 v.addEventListener('ratechange', () => { if (state.applyRate && Math.abs(v.playbackRate - state.rate) >= 0.01) { setTimeout(() => applyRateTo(v), 30); } }); // 拖进度 / 切集后重新套用 v.addEventListener('loadedmetadata', () => applyRateTo(v)); v.addEventListener('play', () => applyRateTo(v)); } applyRateTo(v); }); paintRate(videos.length > 0); } function startRateWatch() { if (rateWatch) return; rateWatch = setInterval(scanVideos, 900); scanVideos(); } // ---------- 2. 文件清单 ---------- function collectRows() { const rows = new Set(); // 百度网盘列表行常见特征 const sels = [ '[class*="file-list"] [class*="item"]', '[class*="filelist"] [class*="item"]', 'li[class*="item"]', 'tr[class*="item"]', '[data-id][class*="row"]', 'dd[class*="item"]', ]; for (const s of sels) { document.querySelectorAll(s).forEach((r) => { if (visible(r) && (r.innerText || '').trim().length > 2) rows.add(r); }); } return [...rows]; } function parseRow(row) { const txt = (row.innerText || '').replace(/\s+/g, ' ').trim(); const sizeM = txt.match(/[\d.]+\s*(?:B|KB|MB|GB|TB)/i); const timeM = txt.match(/\d{4}[-/]\d{1,2}[-/]\d{1,2}(?:\s+\d{1,2}:\d{2})?/); // 文件名取行内 title 或第一段 let name = ''; const t = row.querySelector('[title]'); if (t) name = (t.getAttribute('title') || '').trim(); if (!name) { name = txt.split(' ')[0] || ''; } // 去掉名字里混入的大小/时间 name = name.replace(/[\d.]+\s*(?:B|KB|MB|GB|TB)/i, '').trim(); return { name, size: sizeM ? sizeM[0] : '', time: timeM ? timeM[0] : '' }; } function exportCSV() { const list = collectRows() .map(parseRow) .filter((x) => x.name && x.name.length < 260); if (!list.length) { toast('没识别到文件行,先进入一个文件夹'); return; } const head = '文件名,大小,修改时间\n'; const body = list .map((x) => [x.name, x.size, x.time].map((v) => '"' + String(v).replace(/"/g, '""') + '"').join(',')) .join('\n'); const blob = new Blob(['\ufeff' + head + body], { type: 'text/csv;charset=utf-8' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = '百度网盘清单_' + new Date().toISOString().slice(0, 10) + '.csv'; a.click(); setTimeout(() => URL.revokeObjectURL(a.href), 4000); toast('已导出 ' + list.length + ' 条'); } function folderStats() { const list = collectRows().map(parseRow); const toBytes = (s) => { const m = String(s).match(/([\d.]+)\s*(B|KB|MB|GB|TB)/i); if (!m) return 0; const unit = { B: 1, KB: 1024, MB: 1048576, GB: 1073741824, TB: 1099511627776 }; return parseFloat(m[1]) * (unit[m[2].toUpperCase()] || 1); }; const total = list.reduce((a, x) => a + toBytes(x.size), 0); const fmt = (b) => { if (b >= 1099511627776) return (b / 1099511627776).toFixed(2) + ' TB'; if (b >= 1073741824) return (b / 1073741824).toFixed(2) + ' GB'; if (b >= 1048576) return (b / 1048576).toFixed(2) + ' MB'; if (b >= 1024) return (b / 1024).toFixed(1) + ' KB'; return b + ' B'; }; toast('当前列表 ' + list.length + ' 项,合计 ' + fmt(total)); } // ---------- 3. 弹窗净化 ---------- function closeClientPopup() { const dlg = document.querySelectorAll('[class*="dialog"], [class*="modal"], [class*="popup"], [role="dialog"]'); for (const d of dlg) { if (!visible(d)) continue; if (!CLIENT_POPUP_RE.test(d.innerText || '')) continue; const btns = d.querySelectorAll('button, [class*="close"], [class*="cancel"], span[role="button"], i[class*="close"]'); for (const b of btns) { const t = (b.getAttribute('title') || b.textContent || '').replace(/\s+/g, ''); if (/取消|关闭|以后再说|暂不|继续使用网页|不再提示/.test(t) || /close/i.test(b.className || '')) { b.click(); return true; } } } return false; } // ---------- 界面 ---------- let panel = null; function buildPanel() { if (panel || !document.body) return; const css = document.createElement('style'); css.textContent = ` #bdpan-tool-panel{position:fixed;right:16px;bottom:16px;z-index:2147483000;width:224px;padding:12px; border-radius:12px;background:#fff;border:1px solid rgba(0,0,0,.08);box-shadow:0 8px 28px rgba(0,0,0,.14); font:12px/1.6 -apple-system,"Segoe UI","Microsoft YaHei",sans-serif;color:#1d2129;user-select:none} #bdpan-tool-panel .bp-t{font-weight:700;margin-bottom:8px;display:flex;justify-content:space-between;align-items:center} #bdpan-tool-panel .bp-collapse{cursor:pointer;color:#8a8f99;padding:0 4px} #bdpan-tool-panel .bp-row{display:flex;gap:4px;flex-wrap:wrap;margin-bottom:6px} #bdpan-tool-panel .bp-row button{flex:1 0 30%;padding:5px 0;border-radius:7px;cursor:pointer;font-size:11.5px; border:1px solid rgba(0,0,0,.1);background:#f6f7f9;color:#1d2129} #bdpan-tool-panel .bp-row button:hover{background:#eceef2} #bdpan-tool-panel .bp-row button.on{background:#3f7dff;border-color:#3f7dff;color:#fff} #bdpan-tool-panel .bp-wide{width:100%;margin-top:2px;padding:7px 0;border-radius:8px;cursor:pointer; border:1px solid rgba(0,0,0,.1);background:#f6f7f9;color:#1d2129;font-size:12px} #bdpan-tool-panel .bp-wide:hover{background:#eceef2} #bdpan-tool-panel .bp-hint{margin-top:8px;font-size:11px;color:#8a8f99;line-height:1.5} #bdpan-tool-panel.folded .bp-body{display:none} #bdpan-toast{position:fixed;left:50%;bottom:76px;transform:translateX(-50%);z-index:2147483000; padding:9px 16px;border-radius:999px;background:rgba(24,28,36,.9);color:#fff; font:12px/1.4 -apple-system,"Segoe UI","Microsoft YaHei",sans-serif;pointer-events:none;opacity:0; transition:opacity .2s} #bdpan-toast.show{opacity:1}`; document.head.appendChild(css); panel = document.createElement('div'); panel.id = 'bdpan-tool-panel'; panel.innerHTML = [ '