// ==UserScript== // @name 下载工具助手(迅雷 / 电驴 /蜈蚣/ BT / 自定义) // @name:en Download Helper (Thunder / eMule / Centipede / BT / Custom) // @namespace https://workbuddy.cn // @version 1.0.0 // @description 识别网页中的下载链接,一键用迅雷、电驴、BT 等下载工具拉起下载;带悬浮面板与菜单命令。 // @author WorkBuddy // @match *://*/* // @grant GM_setClipboard // @grant GM_registerMenuCommand // @grant GM_addStyle // @grant GM_notification // @run-at document-idle // @license MIT // ==/UserScript== (function () { 'use strict'; /* ===================== 配置区 ===================== */ const CONFIG = { // 视作“直链”的文件后缀(会被列进面板,可用迅雷拉起) fileExts: [ 'zip', 'rar', '7z', 'exe', 'msi', 'iso', 'dmg', 'tar', 'gz', 'tgz', 'xz', 'mp4', 'mkv', 'avi', 'mov', 'wmv', 'flv', 'mp3', 'flac', 'wav', 'ape', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'apk', 'torrent' ], // 悬浮面板停靠位置:'left' | 'right' panelPos: 'right', // 是否自动扫描页面全部链接 autoScan: true, // 文件蜈蚣(File Centipede) 拉起协议前缀: // 未加密地址用 'fileu:0',加密地址用 'filec:'(加密需软件密钥,浏览器侧用 fileu 即可拉起下载任务) // 见官网 https://www.filecxx.com “Create custom address (filec, fileu)” // 该软件同时还原生支持 thunder://、flashget://、qqdl://、magnet:、ed2k:// 等协议 filecxxScheme: 'fileu:0', // 是否在链接上悬停时弹出小工具条 showHover: true, }; /* ===================== 工具函数 ===================== */ const utf8ToBase64 = (s) => btoa(unescape(encodeURIComponent(s))); const base64ToUtf8 = (b) => decodeURIComponent(escape(atob(b))); // 迅雷专用链接:把普通 URL 包成 AAZZ 再 base64 function toThunder(url) { return 'thunder://' + utf8ToBase64('AA' + url + 'ZZ'); } function fromThunder(thunderUrl) { try { const s = base64ToUtf8(thunderUrl.replace(/^thunder:\/\//i, '')); return s.replace(/^AA/, '').replace(/ZZ$/, ''); } catch (e) { return thunderUrl; } } // 用自定义协议拉起本地下载工具 function launch(protocolUrl) { const a = document.createElement('a'); a.href = protocolUrl; a.style.display = 'none'; document.body.appendChild(a); a.click(); document.body.removeChild(a); // 兜底:部分浏览器对 点击不响应协议时,再尝试直接跳转 setTimeout(() => { try { window.location.href = protocolUrl; } catch (e) {} }, 50); } function notify(msg) { if (typeof GM_notification === 'function') { GM_notification({ text: String(msg), title: '下载工具助手' }); } else { console.log('[下载工具助手]', msg); } } /* ===================== 链接识别 ===================== */ function isDownloadLink(href) { if (!href) return false; if (/^(magnet|ed2k|thunder):/i.test(href)) return true; try { const u = new URL(href, location.href); const path = u.pathname.toLowerCase(); if (/\.torrent$/i.test(path)) return true; return CONFIG.fileExts.some(ext => path.endsWith('.' + ext)); } catch (e) { return false; } } function collectLinks() { const set = new Set(); if (CONFIG.autoScan) { document.querySelectorAll('a[href]').forEach(a => { if (isDownloadLink(a.href)) set.add(a.href); }); } return Array.from(set); } /* ===================== 动作 ===================== */ function sendThunder(url) { let target; if (/^thunder:/i.test(url)) target = url; else if (/^(magnet|ed2k):/i.test(url)) target = url; // 迅雷可直接吃这些协议 else target = toThunder(url); launch(target); notify('已尝试用迅雷拉起:' + url); } function sendEmule(url) { if (/^ed2k:/i.test(url)) { launch(url); notify('已尝试用电驴(eMule)拉起:' + url); } else { notify('电驴需要 eD2k 哈希链接(ed2k://...),当前链接不是:' + url); } } function sendBT(url) { if (/^magnet:/i.test(url) || /\.torrent$/i.test(url)) { launch(url); notify('已尝试用 BT 客户端拉起:' + url); } else { notify('BT 需要 magnet: 或 .torrent 链接,当前:' + url); } } // 文件蜈蚣(File Centipede) 自定义协议 // 未加密地址格式:fileu:0 + base64({"uri":"<下载链接>"}) // 例:fileu:0eyJ1cmkiOiJodHRwczovL2V4YW1wbGUuY29tL2ZpbGUuemlwIn0= // 该软件同时原生支持 thunder://、flashget://、qqdl://、magnet:、ed2k:// 等协议 function toFileCentipede(url) { const json = JSON.stringify({ uri: url }); return CONFIG.filecxxScheme + utf8ToBase64(json); } function sendCentipede(url) { launch(toFileCentipede(url)); notify('已尝试用文件蜈蚣(File Centipede)拉起:' + url); } function copyLink(url) { if (typeof GM_setClipboard === 'function') { GM_setClipboard(url, 'text'); } else if (navigator.clipboard) { navigator.clipboard.writeText(url); } notify('已复制:' + url); } /* ===================== UI:常驻侧栏面板 ===================== */ const STYLE = ` #dh-panel{position:fixed;top:80px;${CONFIG.panelPos}:12px;z-index:2147483647; width:260px;max-height:70vh;overflow:auto;background:#fff;border:1px solid #e0e0e0; border-radius:8px;box-shadow:0 4px 16px rgba(0,0,0,.18);font:12px/1.5 -apple-system,Segoe UI,Roboto,'Microsoft YaHei',sans-serif;color:#222;} #dh-panel h3{margin:0;padding:8px 10px;background:#2d7ff9;color:#fff;font-size:13px; cursor:pointer;border-radius:8px 8px 0 0;display:flex;justify-content:space-between;align-items:center;} #dh-panel .dh-body{padding:8px;} #dh-panel .dh-all{display:flex;gap:6px;margin-bottom:8px;flex-wrap:wrap;} #dh-panel button{font-size:12px;border:1px solid #ccc;background:#f5f5f5;border-radius:4px; padding:3px 8px;cursor:pointer;} #dh-panel button:hover{background:#e8e8e8;} #dh-panel .dh-item{border-top:1px dashed #eee;padding:6px 0;} #dh-panel .dh-link{word-break:break-all;color:#1565c0;font-size:11px;margin-bottom:4px;display:block;} #dh-panel .dh-acts{display:flex;gap:4px;flex-wrap:wrap;} #dh-panel .dh-empty{color:#888;padding:10px;} `; function buildPanel() { if (typeof GM_addStyle === 'function') GM_addStyle(STYLE); else { const s = document.createElement('style'); s.textContent = STYLE; document.head.appendChild(s); } const panel = document.createElement('div'); panel.id = 'dh-panel'; panel.innerHTML = `

下载工具助手[收起]

`; document.body.appendChild(panel); panel.querySelector('#dh-toggle').addEventListener('click', () => { const body = panel.querySelector('#dh-body'); const hidden = body.style.display === 'none'; body.style.display = hidden ? 'block' : 'none'; panel.querySelector('#dh-toggle').textContent = hidden ? '[收起]' : '[展开]'; if (hidden) renderPanel(); }); // 事件委托:只绑定一次,避免重复渲染时叠加监听器 panel.querySelector('#dh-body').addEventListener('click', (e) => { const btn = e.target.closest('button'); if (!btn) return; const act = btn.dataset.act; const i = btn.dataset.i; const links = collectLinks(); if (act === 'all-thunder') links.forEach(sendThunder); else if (act === 'all-bt') links.forEach(sendBT); else if (act === 'copy-all') links.forEach(copyLink); else if (act === 'rescan') renderPanel(); else if (i !== undefined) { const u = links[+i]; if (act === 'thunder') sendThunder(u); else if (act === 'emule') sendEmule(u); else if (act === 'bt') sendBT(u); else if (act === 'centipede') sendCentipede(u); else if (act === 'copy') copyLink(u); } }); renderPanel(); } function renderPanel() { const body = document.getElementById('dh-body'); if (!body || body.style.display === 'none') return; const links = collectLinks(); if (!links.length) { body.innerHTML = '
本页未检测到下载链接。
'; return; } const allBtns = `
`; const items = links.map((u, i) => `
${u}
`).join(''); body.innerHTML = allBtns + items; } /* ===================== UI:悬停工具条(可选) ===================== */ function initHover() { if (!CONFIG.showHover) return; const bar = document.createElement('div'); bar.id = 'dh-hover'; Object.assign(bar.style, { position: 'fixed', zIndex: 2147483647, display: 'none', gap: '4px', background: '#222', padding: '4px', borderRadius: '6px', boxShadow: '0 2px 8px rgba(0,0,0,.3)' }); ['迅雷', '电驴', 'BT', '蜈蚣', '复制'].forEach(label => { const b = document.createElement('button'); b.textContent = label; Object.assign(b.style, { fontSize: '12px', border: 'none', borderRadius: '4px', padding: '3px 8px', cursor: 'pointer', color: '#fff', background: '#2d7ff9' }); b.addEventListener('click', () => { const url = bar.dataset.url; if (label === '迅雷') sendThunder(url); else if (label === '电驴') sendEmule(url); else if (label === 'BT') sendBT(url); else if (label === '蜈蚣') sendCentipede(url); else copyLink(url); bar.style.display = 'none'; }); bar.appendChild(b); }); document.body.appendChild(bar); let hideTimer; document.addEventListener('mouseover', (e) => { const a = e.target.closest('a[href]'); if (a && isDownloadLink(a.href)) { clearTimeout(hideTimer); bar.dataset.url = a.href; bar.style.display = 'flex'; const r = a.getBoundingClientRect(); bar.style.top = (window.scrollY + r.bottom + 4) + 'px'; bar.style.left = (window.scrollX + r.left) + 'px'; } }); document.addEventListener('mouseout', (e) => { const to = e.relatedTarget; if (e.target.closest('a[href]') && !(to && to.closest && to.closest('#dh-hover'))) { hideTimer = setTimeout(() => { bar.style.display = 'none'; }, 300); } }); bar.addEventListener('mouseenter', () => clearTimeout(hideTimer)); bar.addEventListener('mouseleave', () => { bar.style.display = 'none'; }); } /* ===================== 菜单命令 ===================== */ if (typeof GM_registerMenuCommand === 'function') { GM_registerMenuCommand('扫描本页下载链接', () => { buildPanel(); const p = document.getElementById('dh-panel'); if (p) p.scrollIntoView(); notify('已扫描,共 ' + collectLinks().length + ' 个链接。'); }); GM_registerMenuCommand('复制全部为迅雷链接', () => { const links = collectLinks(); const thunderLinks = links.map(u => /^thunder:/i.test(u) ? u : toThunder(u)); if (typeof GM_setClipboard === 'function') GM_setClipboard(thunderLinks.join('\n'), 'text'); notify('已复制 ' + thunderLinks.length + ' 条迅雷链接。'); }); } /* ===================== 启动 ===================== */ function main() { buildPanel(); initHover(); // 动态页面:轻量轮询补充 let lastCount = -1; setInterval(() => { const n = collectLinks().length; if (n !== lastCount) { lastCount = n; renderPanel(); } }, 3000); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', main); } else { main(); } })();