Greasy Fork

Greasy Fork is available in English.

威软全能网盘增强助手 (Open API 终极直连版)

支持夸克、百度、阿里云盘。修正 AList 最新 API 网关,完美适配 JWT 超长 Token,满速提取直链。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         威软全能网盘增强助手 (Open API 终极直连版)
// @namespace    Weiruan-Pan-Helper
// @version      7.2.0
// @description  支持夸克、百度、阿里云盘。修正 AList 最新 API 网关,完美适配 JWT 超长 Token,满速提取直链。
// @author       威软科技
// @license      MIT
// @icon         https://pan.quark.cn/favicon.ico
// @match        *://pan.quark.cn/*
// @match        *://pan.baidu.com/*
// @match        *://yun.baidu.com/*
// @match        *://*.aliyundrive.com/*
// @match        *://*.alipan.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setClipboard
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @grant        unsafeWindow
// @run-at       document-end
// @connect      drive.quark.cn
// @connect      pan.baidu.com
// @connect      openapi.alipan.com
// @connect      api.alistgo.com
// @connect      api.nn.ci
// @homepage     https://github.com/weiruankeji2025/weiruan-quark
// ==/UserScript==

(function() {
    'use strict';

    const CONFIG = {
        VERSION: "7.2.0",
        DEBUG: true, 
        UA_ALIYUN: "AliApp(Yundrive/4.0.0)",
        UA_QUARK: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) quark-cloud-drive/2.5.20 Chrome/100.0.4896.160",
        UA_BAIDU: "netdisk;7.0.3.2;PC;PC-Windows;10.0.19045",
    };

    const State = {
        fileMemoryMap: new Map(),
        aliToken: GM_getValue('weiruan_ali_token', ''),
    };

    const Utils = {
        log: (...args) => { if (CONFIG.DEBUG) console.log('[威软全能助手]', ...args); },
        formatSize: (bytes) => { if (!bytes) return '0 B'; const k = 1024, i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + ['B', 'KB', 'MB', 'GB', 'TB'][i]; },
        getFileIcon: (fn) => {
            const ext = (fn || '').split('.').pop().toLowerCase();
            if (['mp4', 'mkv', 'avi', 'mov'].includes(ext)) return '🎬';
            if (['mp3', 'wav', 'flac'].includes(ext)) return '🎵';
            if (['zip', 'rar', '7z'].includes(ext)) return '📦';
            return '📄';
        },
        post: (url, data, customHeaders = {}) => new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "POST", url: url,
                headers: Object.assign({"Content-Type": "application/json"}, customHeaders),
                data: typeof data === 'string' ? data : JSON.stringify(data), responseType: 'json',
                onload: res => res.status >= 200 && res.status < 300 ? resolve(res.response) : reject(res),
                onerror: reject
            });
        }),
        toast: (msg, type = 'success', duration = 4000) => {
            if (!document.body) return;
            document.querySelector('.weiruan-toast')?.remove();
            const div = document.createElement('div'); div.className = 'weiruan-toast'; div.innerText = msg;
            const colors = { success: '#764ba2', error: '#ff4444', info: '#00bbff', warning: '#ffbb00' };
            div.style.cssText = `position: fixed; top: 80px; left: 50%; transform: translateX(-50%); background: ${colors[type] || colors.success}; color: ${type==='warning'?'#333':'white'}; padding: 16px 28px; border-radius: 12px; z-index: 2147483649; font-size: 15px; font-weight: bold; box-shadow: 0 10px 30px rgba(0,0,0,0.3); animation: weiruan-toast-in 0.3s ease-out; font-family: -apple-system, sans-serif; white-space: pre-line; text-align: center;`;
            document.body.appendChild(div);
            setTimeout(() => { div.style.animation = 'weiruan-toast-out 0.3s ease-out forwards'; setTimeout(() => div.remove(), 300); }, duration);
        }
    };

    // ==================== 🔑 Open API 核心授权引擎 ====================
    const AliOpenAPI = {
        async getAccessToken() {
            if (!State.aliToken) return null;
            Utils.toast('正在通过 AList 开放网关验证身份...', 'info', 2000);
            try {
                // 【核心修复】:使用最新可用的 alistgo 网关
                const res = await Utils.post('https://api.alistgo.com/alist/ali_open/token', {
                    grant_type: "refresh_token",
                    refresh_token: State.aliToken
                });
                if (res && res.access_token) {
                    if (res.refresh_token) {
                        State.aliToken = res.refresh_token;
                        GM_setValue('weiruan_ali_token', res.refresh_token);
                    }
                    return res.access_token;
                }
            } catch (e) {
                Utils.log("Token 刷新失败,请检查是否过期或复制错误", e);
            }
            return null;
        },

        async getDownloadUrl(driveId, fileId, accessToken) {
            try {
                const res = await Utils.post('https://openapi.alipan.com/adrive/v1.0/openFile/getDownloadUrl', {
                    drive_id: driveId,
                    file_id: fileId,
                    expire_sec: 14400 // 4小时有效
                }, { "Authorization": "Bearer " + accessToken });
                if (res && res.url) return res.url;
            } catch (e) {
                Utils.log(`获取文件 ${fileId} 直链失败`, e);
            }
            return null;
        }
    };

    // ==================== 适配器 ====================
    const Adapters = {
        quark: {
            name: "夸克网盘", matches: ['pan.quark.cn'], ua: CONFIG.UA_QUARK,
            getSelectedFiles: function() {
                const files = new Map();
                document.querySelectorAll('.ant-checkbox-wrapper-checked, .ant-checkbox-checked, input[type="checkbox"]:checked').forEach(el => {
                    if (el.closest('.ant-table-thead')) return;
                    const row = el.closest('.ant-table-row, .file-list-item, [data-fid]');
                    const fid = row?.getAttribute('data-fid') || row?.getAttribute('data-file-id');
                    if (fid) files.set(fid, { fid, name: row.querySelector('.file-name')?.innerText.trim() || '未命名', isDir: false, size: 0, download_url: '' });
                });
                return Array.from(files.values());
            },
            processFiles: async f => f
        },
        baidu: {
            name: "百度网盘", matches: ['pan.baidu.com', 'yun.baidu.com'], ua: CONFIG.UA_BAIDU,
            getSelectedFiles: function() {
                try {
                    return unsafeWindow.require('system-core:context/context.js').instanceForSystem.list.getSelected().map(item => ({
                        fid: item.fs_id, name: item.server_filename, isDir: item.isdir === 1, size: item.size, download_url: ''
                    }));
                } catch (e) { return []; }
            },
            processFiles: async f => f
        },
        aliyun: {
            name: "阿里云盘 (OpenAPI版)", matches: ['aliyundrive.com', 'alipan.com'], ua: CONFIG.UA_ALIYUN,
            getSelectedFiles: function() {
                const files = new Map();
                const findFilesInFiber = (obj, visited = new Set(), depth = 0) => {
                    if (!obj || typeof obj !== 'object' || depth > 15) return;
                    if (visited.has(obj)) return; visited.add(obj);
                    if (obj.file_id && obj.name && typeof obj.file_id === 'string' && obj.file_id.length > 5) State.fileMemoryMap.set(obj.name.trim(), obj);
                    for (let key in obj) {
                        if (key === 'return' || key === 'child' || key === 'sibling' || key.startsWith('__')) continue;
                        try { findFilesInFiber(obj[key], visited, depth + 1); } catch(e) {}
                    }
                };

                document.querySelectorAll('#root, #app, [class*="layout"], [class*="container"]').forEach(el => {
                    const key = Object.keys(el).find(k => k.startsWith('__reactFiber$'));
                    if (key) findFilesInFiber(el[key]);
                });

                document.querySelectorAll('input[type="checkbox"]:checked, [aria-checked="true"], [class*="checked"], .icon-checked').forEach(el => {
                    if (el.closest('thead') || el.closest('[class*="header"]')) return;
                    let matched = false, currentEl = el;

                    for (let i = 0; i < 5 && currentEl; i++) {
                        const titleVal = currentEl.getAttribute('title') || currentEl.getAttribute('data-title');
                        if (titleVal && State.fileMemoryMap.has(titleVal.trim())) {
                            const memObj = State.fileMemoryMap.get(titleVal.trim());
                            files.set(memObj.file_id, { fid: memObj.file_id, drive_id: memObj.drive_id, name: memObj.name, isDir: memObj.type === 'folder', size: memObj.size || 0 });
                            matched = true; break;
                        }
                        currentEl = currentEl.parentElement;
                    }

                    if (!matched) {
                        currentEl = el;
                        const cleanStr = str => (str || '').replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, '');
                        for (let i = 0; i < 10 && currentEl; i++) {
                            const cleanRowText = cleanStr(currentEl.textContent);
                            if (cleanRowText.length > 1) {
                                for (let [memName, memObj] of Array.from(State.fileMemoryMap.entries()).sort((a,b) => b[0].length - a[0].length)) {
                                    const cName = cleanStr(memName);
                                    if (cName.length > 0 && cleanRowText.includes(cName.substring(0, 5))) {
                                        files.set(memObj.file_id, { fid: memObj.file_id, drive_id: memObj.drive_id, name: memObj.name, isDir: memObj.type === 'folder', size: memObj.size || 0 });
                                        matched = true; break;
                                    }
                                }
                            }
                            if (matched) break; currentEl = currentEl.parentElement;
                        }
                    }
                });
                return Array.from(files.values());
            },
            processFiles: async function(files) {
                const validFiles = files.filter(f => !f.isDir);
                if (validFiles.length < files.length) Utils.toast('已过滤文件夹,OpenAPI 仅支持提取文件直链。', 'warning');
                if (validFiles.length === 0) return null;

                const accessToken = await AliOpenAPI.getAccessToken();
                if (!accessToken) {
                    Utils.toast('⚠️ Token 无效或已过期,请重新配置!', 'error');
                    UI.showAuthWindow();
                    return null;
                }

                Utils.toast(`正在向阿里云盘服务器申请 ${validFiles.length} 个满速直链...`, 'info');
                let readyFiles = [];
                for (let f of validFiles) {
                    const url = await AliOpenAPI.getDownloadUrl(f.drive_id, f.fid, accessToken);
                    if (url) readyFiles.push({ ...f, file_name: f.name, download_url: url });
                }
                if (readyFiles.length === 0) { Utils.toast('提取失败,Token可能过期,请重新配置。', 'error'); return null; }
                return readyFiles;
            }
        }
    };

    const DriveManager = {
        currentAdapter: null,
        init() {
            const host = location.host;
            for (const key in Adapters) {
                if (Adapters[key].matches.some(m => host.includes(m))) {
                    this.currentAdapter = Adapters[key]; break;
                }
            }
        },
        async runExtraction() {
            if (!this.currentAdapter) return;
            
            if (this.currentAdapter.name.includes("阿里云盘") && !State.aliToken) {
                UI.showAuthWindow();
                return;
            }

            const btn = document.getElementById('weiruan-btn');
            try {
                if (btn) { btn.innerHTML = `<span class="weiruan-spinner"></span> 处理中`; btn.disabled = true; }
                const initialFiles = this.currentAdapter.getSelectedFiles();
                
                if (initialFiles.length === 0) {
                    Utils.toast('⚠️ 请先在页面上勾选需要下载的文件!', 'error');
                    return;
                }

                const resultData = await this.currentAdapter.processFiles(initialFiles);
                if (resultData && resultData.length > 0) {
                    UI.showResultWindow(resultData);
                }
            } catch (e) {
                Utils.log("提取过程出错", e);
            } finally {
                if (btn) { btn.innerHTML = `<span class="weiruan-icon">⚡</span> 威软提取`; btn.disabled = false; }
            }
        }
    };

    // ==================== 界面 UI ====================
    const UI = {
        injectStyles: () => {
            GM_addStyle(`
                @keyframes weiruan-toast-in { from { opacity: 0; transform: translate(-50%, -20px); } to { opacity: 1; transform: translate(-50%, 0); } }
                @keyframes weiruan-toast-out { from { opacity: 1; transform: translate(-50%, 0); } to { opacity: 0; transform: translate(-50%, -20px); } }
                @keyframes weiruan-slide-in { from { opacity: 0; transform: scale(0.9); } to { opacity: 1; transform: scale(1); } }
                @keyframes weiruan-spin { to { transform: rotate(360deg); } }
                .weiruan-spinner { display: inline-block; width: 14px; height: 14px; border: 2px solid rgba(255,255,255,0.3); border-top-color: white; border-radius: 50%; animation: weiruan-spin 0.8s linear infinite; margin-right: 6px; vertical-align: middle; }
                .weiruan-btn { position: fixed; top: 50%; left: 0; transform: translateY(-50%); z-index: 2147483647; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; font-size: 14px; font-weight: 600; padding: 14px 20px; border: none; border-radius: 0 25px 25px 0; cursor: pointer; box-shadow: 0 4px 15px rgba(102, 126, 234, 0.5); transition: all 0.3s ease; }
                .weiruan-btn:hover { padding-left: 26px; box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6); }
                .weiruan-menu { position: fixed; top: calc(50% + 50px); left: 0; transform: translateY(-50%); z-index: 2147483646; display: flex; flex-direction: column; gap: 5px; opacity: 0; pointer-events: none; transition: all 0.3s ease; }
                .weiruan-btn:hover + .weiruan-menu, .weiruan-menu:hover { opacity: 1; pointer-events: auto; left: 5px; }
                .weiruan-menu-item { background: rgba(255,255,255,0.95); color: #333; padding: 10px 16px; border-radius: 20px; font-size: 13px; font-weight: bold; cursor: pointer; box-shadow: 0 2px 10px rgba(0,0,0,0.1); transition: all 0.2s; white-space: nowrap; border: 1px solid #eee; }
                .weiruan-menu-item:hover { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; transform: translateX(5px); }
                .weiruan-modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.6); z-index: 2147483648; display: flex; align-items: center; justify-content: center; backdrop-filter: blur(5px); }
                .weiruan-modal { background: #fff; width: 720px; max-width: 92%; border-radius: 16px; box-shadow: 0 25px 50px rgba(0,0,0,0.3); display: flex; flex-direction: column; overflow: hidden; font-family: sans-serif; animation: weiruan-slide-in 0.3s ease-out; }
                .weiruan-modal-header { padding: 18px 24px; display: flex; justify-content: space-between; align-items: center; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; }
                .weiruan-modal-title { margin: 0; font-size: 18px; font-weight: 600; }
                .weiruan-modal-close { cursor: pointer; font-size: 28px; line-height: 1; }
                .weiruan-toolbar { padding: 12px 24px; background: #f8f9ff; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center;}
                .weiruan-btn-group { display: flex; gap: 8px; }
                .weiruan-action-btn { padding: 8px 16px; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 500; color: white; transition: transform 0.2s;}
                .weiruan-action-btn:hover { transform: translateY(-1px); }
                .weiruan-action-btn.primary { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
                .weiruan-action-btn.success { background: linear-gradient(135deg, #56ab2f 0%, #a8e063 100%); }
                .weiruan-modal-body { padding: 16px 24px; overflow-y: auto; max-height: 450px; }
                .weiruan-file-item { background: #f9f9f9; padding: 14px 16px; margin-bottom: 10px; border-radius: 10px; border-left: 4px solid #667eea; display: flex; justify-content: space-between; align-items: center; }
                .weiruan-file-info { overflow: hidden; flex: 1; margin-right: 12px; }
                .weiruan-file-name { font-weight: 600; color: #333; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 14px; }
                .weiruan-file-meta { font-size: 12px; color: #888; margin-top: 4px; }
                .weiruan-file-btn { padding: 6px 12px; border: none; border-radius: 5px; cursor: pointer; font-size: 12px; color: white; text-decoration: none; display: inline-block;}
                .weiruan-file-btn.idm { background: linear-gradient(135deg, #56ab2f 0%, #a8e063 100%); }
                .weiruan-file-btn.aria2 { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); }
                .weiruan-input { width: 100%; padding: 12px; border: 2px solid #ddd; border-radius: 8px; font-size: 14px; margin: 15px 0; outline: none; transition: border-color 0.2s; box-sizing: border-box; }
                .weiruan-input:focus { border-color: #667eea; }
                .weiruan-footer { padding: 12px 24px; border-top: 1px solid #eee; text-align: center; font-size: 12px; color: #999; }
            `);
        },
        createFloatButton: () => {
            if (document.getElementById('weiruan-btn')) return;
            const btn = document.createElement('button');
            btn.id = 'weiruan-btn'; btn.className = 'weiruan-btn';
            btn.innerHTML = `<span class="weiruan-icon">⚡</span> 威软提取`;
            btn.onclick = () => DriveManager.runExtraction();
            document.body.appendChild(btn);

            const menu = document.createElement('div');
            menu.className = 'weiruan-menu';
            menu.innerHTML = `<div class="weiruan-menu-item" id="wr-menu-auth">🔑 阿里云盘 OpenAPI 授权</div>`;
            document.body.appendChild(menu);

            document.getElementById('wr-menu-auth').onclick = () => UI.showAuthWindow();
        },

        showAuthWindow: () => {
            document.getElementById('weiruan-modal')?.remove();
            const modal = document.createElement('div');
            modal.id = 'weiruan-modal';
            modal.className = `weiruan-modal-overlay`;

            modal.innerHTML = `
            <div class="weiruan-modal" style="width: 500px;">
                <div class="weiruan-modal-header">
                    <h3 class="weiruan-modal-title">🔐 配置阿里云盘 OpenAPI 授权</h3>
                    <span class="weiruan-modal-close" id="weiruan-modal-close">&times;</span>
                </div>
                <div class="weiruan-modal-body">
                    <div style="background:#fff3cd; color:#856404; padding:12px; border-radius:8px; font-size:13px; margin-bottom:15px; line-height:1.5;">
                        <b>请按以下步骤完成授权(仅需一次,告别限速):</b><br><br>
                        1. 点击下方按钮,前往 AList 官方开放平台。<br>
                        2. 在新页面中使用阿里云盘 APP 扫码。<br>
                        3. 扫码成功后,复制生成的 <b>Refresh Token(长代码)</b>。<br>
                        4. 将其完整粘贴到下方输入框,并点击保存。
                    </div>
                    
                    <button class="weiruan-action-btn primary" id="wr-goto-auth" style="width: 100%; padding: 12px; font-size: 15px; margin-bottom: 10px;">
                        👉 1. 点击前往获取 Refresh Token
                    </button>

                    <input type="text" class="weiruan-input" id="wr-token-input" placeholder="👉 2. 请将获取到的 Token 粘贴在此处..." value="${State.aliToken}">
                    
                    <button class="weiruan-action-btn success" id="wr-save-token" style="width: 100%; padding: 12px; font-size: 15px;">
                        💾 保存 Token 并立即提取
                    </button>
                </div>
            </div>`;
            document.body.appendChild(modal);

            document.getElementById('weiruan-modal-close').addEventListener('click', () => modal.remove());
            
            document.getElementById('wr-goto-auth').addEventListener('click', () => {
                window.open('https://alist.nn.ci/tool/aliyundrive/request', '_blank');
            });

            document.getElementById('wr-save-token').addEventListener('click', () => {
                const tokenVal = document.getElementById('wr-token-input').value.trim();
                // 放宽验证,只要是长字符串就行
                if (tokenVal.length < 50 || !tokenVal.startsWith('ey')) {
                    Utils.toast('Token 格式不正确!请确保复制了完整的以 ey 开头的超长代码。', 'error');
                    return;
                }
                State.aliToken = tokenVal;
                GM_setValue('weiruan_ali_token', tokenVal);
                Utils.toast('✅ 授权保存成功!正在为您提取直链...', 'success');
                modal.remove();
                DriveManager.runExtraction(); 
            });
        },

        showResultWindow: (data) => {
            document.getElementById('weiruan-modal')?.remove();
            const modal = document.createElement('div');
            modal.id = 'weiruan-modal';
            modal.className = `weiruan-modal-overlay`;
            
            const fileListHTML = data.map((f, index) => `
                <div class="weiruan-file-item">
                    <div class="weiruan-file-info">
                        <div class="weiruan-file-name" title="${f.name}"><span>${Utils.getFileIcon(f.file_name)}</span> ${f.file_name}</div>
                        <div class="weiruan-file-meta">${Utils.formatSize(f.size)} | 官方 OpenAPI 满速通道</div>
                    </div>
                    <div style="display:flex; gap:6px;">
                        <a href="${f.download_url.replace(/"/g, '&quot;')}" target="_blank" class="weiruan-file-btn idm">⬇️ 浏览器 / IDM 下载</a>
                        <button class="weiruan-file-btn aria2 weiruan-copy-aria2" data-index="${index}">🚀 复制 aria2</button>
                    </div>
                </div>`).join('');

            modal.innerHTML = `
            <div class="weiruan-modal">
                <div class="weiruan-modal-header">
                    <h3 class="weiruan-modal-title">🚀 满速直链提取成功!(共 ${data.length} 个)</h3>
                    <span class="weiruan-modal-close" id="weiruan-modal-close">&times;</span>
                </div>
                <div>
                    <div class="weiruan-toolbar">
                        <div class="weiruan-toolbar-info">💡 提示:使用 IDM 时若文件名不对,请手动修改后缀名。</div>
                        <div class="weiruan-btn-group">
                            <button class="weiruan-action-btn primary" id="weiruan-copy-all-btn">📦 复制全部直链 URL</button>
                            <button class="weiruan-action-btn success" id="weiruan-copy-aria2-btn">🚀 复制批量 aria2 命令</button>
                        </div>
                    </div>
                    <div class="weiruan-modal-body">${fileListHTML}</div>
                </div>
                <div class="weiruan-footer">威软科技 出品 · 彻底击碎网盘限速</div>
            </div>`;
            document.body.appendChild(modal);

            document.getElementById('weiruan-modal-close').addEventListener('click', () => modal.remove());
            
            const activeUA = CONFIG.UA_ALIYUN;
            const getLinks = () => data.map(f => f.download_url).join('\n');
            const getAria2 = () => data.map(f => `aria2c -c -x 16 -s 16 "${f.download_url}" -o "${f.file_name}" -U "${activeUA}" --header="Cookie: ${document.cookie}"`).join('\n\n');

            document.getElementById('weiruan-copy-all-btn').addEventListener('click', () => { GM_setClipboard(getLinks()); Utils.toast('✅ 全部链接已复制'); });
            document.getElementById('weiruan-copy-aria2-btn').addEventListener('click', () => { GM_setClipboard(getAria2()); Utils.toast('✅ aria2 命令已复制'); });

            modal.querySelectorAll('.weiruan-copy-aria2').forEach(btn => {
                btn.addEventListener('click', (e) => {
                    const f = data[parseInt(e.target.getAttribute('data-index'))];
                    GM_setClipboard(`aria2c -c -x 16 -s 16 "${f.download_url}" -o "${f.file_name}" -U "${activeUA}" --header="Cookie: ${document.cookie}"`);
                    Utils.toast('✅ 单个 aria2 已复制');
                });
            });
        }
    };

    function domReady(fn) {
        if (document.readyState === 'complete' || document.readyState === 'interactive') { setTimeout(fn, 1); } 
        else { document.addEventListener('DOMContentLoaded', fn); }
    }

    domReady(() => {
        // 阻止官方下载按钮事件绑定
        document.body.addEventListener('click', (e) => {
            const t = e.target;
            if ((t.innerText || t.textContent || '').includes('下载') && !t.closest('#weiruan-btn') && !t.closest('#weiruan-modal')) {
                if (location.host.includes('aliyundrive.com') || location.host.includes('alipan.com')) {
                    Utils.toast('🛑 要使用满速提取,请点击左侧紫色的\n【⚡ 威软提取】悬浮按钮!', 'warning', 4000);
                }
            }
        }, true);
        
        UI.injectStyles();
        UI.createFloatButton();
        DriveManager.init();
        console.log(`[威软全能助手] v${CONFIG.VERSION} 网关修正版已启动`);
    });
})();