Greasy Fork is available in English.
自动清理网盘分享链接中的多余字符,自动复制纯净链接,并自动关闭分享弹窗。修复百度网盘自动提取失效问题,修复迅雷灰屏问题,针对UC网盘“链接已生成”弹窗无法关闭的问题进行专项修复。
// ==UserScript==
// @name 网盘分享链接自动复制与自动关闭分享链接弹窗 支持百度网盘 夸克网盘 迅雷网盘 UC网盘 123网盘
// @namespace https://www.souyuanzhan.com
// @version 2.6
// @description 自动清理网盘分享链接中的多余字符,自动复制纯净链接,并自动关闭分享弹窗。修复百度网盘自动提取失效问题,修复迅雷灰屏问题,针对UC网盘“链接已生成”弹窗无法关闭的问题进行专项修复。
// @author 搜源站 & Www.SouYuanZhan.Com
// @match https://pan.xunlei.com/*
// @match https://drive.uc.cn/*
// @match https://pan.quark.cn/*
// @match https://pan.baidu.com/*
// @match https://yun.baidu.com/*
// @match https://www.123pan.com/*
// @grant GM_setClipboard
// @grant GM_addStyle
// @license MIT
// ==/UserScript==
(function() {
'use strict';
let lastCopiedText = '';
const CLOSE_RETRY_TIMES = 5;
const CLOSE_INTERVAL = 300;
// 辅助函数:判断元素是否可见
function isVisible(el) {
if (!el) return false;
const style = window.getComputedStyle(el);
if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') return false;
const rect = el.getBoundingClientRect();
return rect.width > 0 && rect.height > 0;
}
// 网站配置
const sitePatterns = [
{
name: '迅雷网盘',
domain: 'pan.xunlei.com',
regex: /(https:\/\/pan\.xunlei\.com\/s\/[^?\s]+\?pwd=[^#\s]+)/,
closeSelector: `.xl-dialog-close, .kui-dialog-close, .title-box .close, [class*="dialog"] [class*="close"], button[title="关闭"], svg[class*="close"]`
},
{
name: '百度网盘',
domain: 'baidu.com',
regex: /(https:\/\/pan\.baidu\.com\/s\/[^\s]+)/,
closeSelector: `.dialog-header .close, .b-dialog-close, .dialog-close, a[title="关闭"], div[title="关闭"], .close-btn, [class*="dialog"] .icon-close, .icon-close-small`
},
{
name: 'UC网盘',
domain: 'drive.uc.cn',
regex: /(https:\/\/drive\.uc\.cn\/s\/[^?\s]+)/,
// 修复:添加 .bind-real-name-close-btn 以及更多通用关闭类名
closeSelector: `.bind-real-name-close-btn, .dialog-close, .icon-close, .ant-modal-close, .zeus-dialog-close, .uc-share-dialog-close, .zeus-icon-close, [class*="close-icon"], .ant-modal-close-x, button[aria-label="关闭"], .close-btn, [class*="dialog"] [class*="close"]`
},
{
name: '夸克网盘',
domain: 'pan.quark.cn',
regex: /(https:\/\/pan\.quark\.cn\/s\/[^\s]+)/,
closeSelector: `.ant-modal-close, .ant-modal-close-x, .share-dialog-close, [aria-label="Close"], .icon-close`
},
{
name: '123网盘',
domain: '123pan.com',
regex: /(https:\/\/www\.123pan\.com\/s\/[^\s]+)/,
closeSelector: `.ant-modal-close, button[aria-label="Close"], .el-dialog__headerbtn`
}
];
// 执行复制并启动关闭流程
function doCopy(cleanLink, type, isAuto = false, siteConfig = null) {
if (!cleanLink || cleanLink === lastCopiedText) return;
GM_setClipboard(cleanLink, 'text');
lastCopiedText = cleanLink;
console.log(`[${type}] 链接已复制:`, cleanLink);
showNotification(`${type} 链接已自动复制`, cleanLink);
if (isAuto && siteConfig) {
attemptToCloseDialog(type, siteConfig);
}
}
// 通用关闭尝试逻辑
function attemptToCloseDialog(type, siteConfig) {
let count = 0;
const maxRetries = type === '迅雷网盘' ? 12 : 10;
const timer = setInterval(() => {
count++;
let closed = false;
// 1. 尝试 CSS 选择器点击
closed = closeDialogBySelector(siteConfig.closeSelector);
// 2. 模拟 ESC (百度常用)
if (!closed && type !== '迅雷网盘') {
closed = pressEscapeKey();
}
// 3. 迅雷特殊遮罩清理
if (type === '迅雷网盘') {
if(!closed) forceCloseXunleiWithRetry();
removeMaskLayer();
}
// 4. 文本按钮兜底
if (!closed) {
let keywords = ['关闭', 'Close', '取消'];
if (type === 'UC网盘') {
// UC 可能出现 "完成" 或 "我知道了"
keywords.push('完成', '我知道了', '确定', '好');
}
if (count > 2 || type === 'UC网盘') {
closed = findAndClickTextButton(keywords);
}
}
// 5. UC网盘特殊深度兜底 (针对复杂嵌套结构)
if (!closed && type === 'UC网盘' && count > 3) {
// 查找弹窗容器内的所有可能关闭元素
const buttons = document.querySelectorAll('div[class*="dialog"] div, div[class*="modal"] div, span');
for (let btn of buttons) {
if (!isVisible(btn)) continue;
// 检查 class 是否包含 close 且不包含 text
const cls = (btn.className || '').toString();
if (cls.includes('close') && !btn.innerText.trim()) {
simulateClick(btn);
closed = true;
break;
}
}
}
// 结束条件
if (closed || count >= maxRetries) {
clearInterval(timer);
if(type === '迅雷网盘') {
removeMaskLayer();
setTimeout(() => {
console.log('迅雷网盘自动刷新页面...');
window.location.reload();
}, 500);
}
}
}, CLOSE_INTERVAL);
}
/**
* 百度特化逻辑
*/
function checkBaiduSpecial() {
if (!window.location.host.includes('baidu.com')) return false;
const inputs = document.querySelectorAll('input[type="text"], input[readonly], textarea');
let linkUrl = '';
let pwd = '';
for (let input of inputs) {
if (!isVisible(input)) continue;
const val = input.value.trim();
if (val.includes('pan.baidu.com/s/')) {
linkUrl = val;
}
else if (val.length === 4 && /^[a-zA-Z0-9]+$/.test(val) && !val.includes('http')) {
pwd = val;
}
}
if (linkUrl) {
let cleanLink = linkUrl;
if (!cleanLink.includes('pwd=') && pwd) {
cleanLink += '?pwd=' + pwd;
}
const siteConfig = sitePatterns.find(s => s.name === '百度网盘');
if (cleanLink !== lastCopiedText) {
doCopy(cleanLink, '百度网盘', true, siteConfig);
return true;
}
}
return false;
}
// 通用DOM扫描
function checkDialogAndCopy() {
if (checkBaiduSpecial()) return;
// 扩大扫描范围,包括 span 和 p,防止部分网站链接不是 input
const linkContainers = document.querySelectorAll(`
input[value*="http"], textarea,
div[class*="url"], div[class*="link"], .share-url,
.ant-input, .el-input__inner,
p, span
`);
for (let el of linkContainers) {
if (!isVisible(el)) continue;
// 获取文本,如果是 input 取 value,否则取 innerText
let text = el.value || el.innerText || '';
if (!text || text.length < 10) continue;
// 简单校验是否包含目标域名
const isTarget = sitePatterns.some(site => text.includes(site.domain));
if (!isTarget) continue;
const result = getCleanLinkInfo(text);
if (result && result.cleanLink !== lastCopiedText) {
doCopy(result.cleanLink, result.type, true, result.config);
break;
}
}
}
function getCleanLinkInfo(text) {
if (!text) return null;
for (let site of sitePatterns) {
if (site.name === '百度网盘' && window.location.host.includes('baidu.com')) continue;
if (window.location.href.includes(site.domain) || text.includes(site.domain)) {
let cleanLink = '';
if (site.regex) {
const match = text.match(site.regex);
if (match) cleanLink = match[1];
}
if (cleanLink) return { cleanLink, type: site.name, config: site };
}
}
return null;
}
function closeDialogBySelector(selector) {
if (!selector) return false;
const selectors = selector.split(',');
for (let sel of selectors) {
const btns = document.querySelectorAll(sel.trim());
for (let btn of btns) {
if (isVisible(btn)) {
if (simulateClick(btn)) return true;
}
}
}
return false;
}
function simulateClick(element) {
if (!element) return false;
try {
// 如果元素被 SVG 或 span 包裹,尝试点击父级
if (['svg', 'path', 'i', 'span', 'img'].includes(element.tagName.toLowerCase())) {
const parent = element.closest('button, a, div[role="button"], [class*="btn"], [class*="close"]');
if (parent) element = parent;
}
element.click();
return true;
} catch (e) { return false; }
}
function pressEscapeKey() {
const event = new KeyboardEvent('keydown', { key: 'Escape', keyCode: 27, which: 27, bubbles: true });
document.body.dispatchEvent(event);
return true;
}
function removeMaskLayer() {
const masks = document.querySelectorAll('.kui-mask, .xl-mask, .ant-modal-mask, [class*="mask"]');
masks.forEach(mask => isVisible(mask) && mask.remove());
if (document.body.style.overflow === 'hidden') document.body.style.overflow = '';
}
function findAndClickTextButton(keywords) {
const candidates = document.querySelectorAll('button, div[role="button"], span, a, div[class*="btn"], div[class*="button"]');
for (let el of candidates) {
if (!isVisible(el)) continue;
const text = (el.innerText || el.textContent || '').trim();
if (text.length > 0 && text.length < 8 && keywords.some(k => text === k)) {
if (simulateClick(el)) return true;
}
}
return false;
}
function forceCloseXunleiWithRetry() {
const buttons = document.querySelectorAll('.dialog-footer .btn, .kui-button, div[class*="cancel"]');
for (let btn of buttons) { if (isVisible(btn) && (btn.innerText||'').includes('取消')) simulateClick(btn); }
}
function showNotification(message, subtext) {
const old = document.getElementById('clean-link-notify');
if (old) old.remove();
const div = document.createElement('div');
div.id = 'clean-link-notify';
div.innerHTML = `<div style="font-weight:bold;">✅ ${message}</div><div style="font-size:12px;opacity:0.8;word-break:break-all;">${subtext}</div>`;
div.style.cssText = `position:fixed;top:12%;right:20px;background:#06a7ff;color:#fff;padding:12px 20px;border-radius:4px;z-index:2147483647;box-shadow:0 4px 12px rgba(0,0,0,0.15);font-family:sans-serif;pointer-events:none;max-width:300px;`;
document.body.appendChild(div);
setTimeout(() => div.remove(), 2500);
}
// 监听复制事件
document.addEventListener('copy', function(e) {
let text = window.getSelection ? window.getSelection().toString() : '';
const result = getCleanLinkInfo(text);
if (result) {
doCopy(result.cleanLink, result.type, true, result.config);
} else if (window.location.host.includes('baidu.com')) {
if (text.includes('pan.baidu.com') && (text.includes('提取码') || text.match(/[a-zA-Z0-9]{4}/))) {
const urlMatch = text.match(/(https:\/\/pan\.baidu\.com\/s\/[^\s]+)/);
const pwdMatch = text.match(/提取码[::]?\s*([a-zA-Z0-9]{4})/);
if (urlMatch) {
let link = urlMatch[1];
if (pwdMatch) link += '?pwd=' + pwdMatch[1];
doCopy(link, '百度网盘', true, sitePatterns.find(s=>s.name==='百度网盘'));
}
}
}
}, true);
const observer = new MutationObserver((mutations) => {
checkDialogAndCopy();
});
setTimeout(() => {
observer.observe(document.body, { childList: true, subtree: true });
checkDialogAndCopy();
}, 1500);
})();