Greasy Fork

Greasy Fork is available in English.

威软全能网盘增强助手

支持夸克、百度、阿里云盘。搭载【无感网络劫持引擎】,精准截获阿里 CDN 真实直链并强行唤起提取面板,彻底粉碎封锁。

当前为 2026-04-24 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         威软全能网盘增强助手
// @namespace    Weiruan-Pan-Helper
// @version      3.3.0
// @description  支持夸克、百度、阿里云盘。搭载【无感网络劫持引擎】,精准截获阿里 CDN 真实直链并强行唤起提取面板,彻底粉碎封锁。
// @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-start
// @connect      drive.quark.cn
// @connect      pan.baidu.com
// @connect      api.aliyundrive.com
// @homepage     https://github.com/weiruankeji2025/weiruan-quark
// ==/UserScript==

(function() {
    'use strict';

    const CONFIG = {
        VERSION: "3.3.0",
        DEBUG: true, 
        HISTORY_MAX: 100,
        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",
        UA_ALIYUN: "AliApp(Yundrive/4.0.0)",
    };

    const State = {
        lang: GM_getValue('weiruan_lang', 'zh'),
        theme: GM_getValue('weiruan_theme', 'auto'),
        history: GM_getValue('weiruan_history', []),
        fileMemoryMap: new Map(), // 存储全页面的文件信息字典
        getLang() { return { 
            title: '威软全能网盘助手', downloadHelper: '下载助手', processing: '处理中...', success: '解析成功', 
            error: '错误', noFiles: '请先勾选需要下载的文件', networkError: '网络请求失败,请检查网络', 
            copied: '已复制', copyAll: '复制全部', copyAria2: '导出 aria2', copyCurl: '导出 cURL', 
            totalSize: '总大小', history: '历史记录', clearHistory: '清空历史', settings: '设置', 
            darkMode: '深色模式', language: '语言', all: '全部', files: '个文件', noHistory: '暂无下载历史', 
            auto: '跟随系统', light: '浅色', dark: '深色'
        }; },
        setLang(lang) { this.lang = lang; GM_setValue('weiruan_lang', lang); },
        setTheme(theme) { this.theme = theme; GM_setValue('weiruan_theme', theme); if(document.body) UI.applyTheme(); },
        isDark() { return this.theme === 'auto' ? window.matchMedia('(prefers-color-scheme: dark)').matches : this.theme === 'dark'; },
        addHistory(files) {
            const newHistory = files.map(f => ({ name: f.file_name, size: f.size, time: Date.now() }));
            this.history = [...newHistory, ...this.history].slice(0, CONFIG.HISTORY_MAX);
            GM_setValue('weiruan_history', this.history);
        },
        clearHistory() { this.history = []; GM_setValue('weiruan_history', []); }
    };

    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]; },
        getFileType: (fn) => { if(!fn) return 'other'; const ext = fn.split('.').pop().toLowerCase(); const t = { video: ['mp4', 'mkv', 'avi', 'mov', 'wmv', 'flv', 'rmvb', 'm4v'], audio: ['mp3', 'wav', 'flac', 'aac', 'ogg', 'wma', 'm4a'], image: ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'ico'], document: ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'md'], archive: ['zip', 'rar', '7z', 'tar', 'gz', 'bz2'] }; for (const [type, exts] of Object.entries(t)) if (exts.includes(ext)) return type; return 'other'; },
        getFileIcon: (fn) => ({ video: '🎬', audio: '🎵', image: '🖼️', document: '📄', archive: '📦', other: '📁' }[Utils.getFileType(fn)] || '📁'),
        generateBatchLinks: (files) => files.map(f => f.download_url).join('\n'),
        generateAria2Commands: (files, ua) => files.map(f => `${f.folderPath ? `mkdir -p "${f.folderPath}" && ` : ''}aria2c -c -x 16 -s 16 "${f.download_url}" -o "${f.fullPath || f.file_name}" -U "${ua}" --header="Cookie: ${document.cookie}"`).join('\n\n'),
        generateCurlCommands: (files, ua) => files.map(f => `${f.folderPath ? `mkdir -p "${f.folderPath}" && ` : ''}curl -L -C - "${f.download_url}" -o "${f.fullPath || f.file_name}" -A "${ua}" -b "${document.cookie}"`).join('\n\n'),
        toast: (msg, type = 'success') => {
            if (!document.body) return;
            document.querySelector('.weiruan-toast')?.remove();
            const div = document.createElement('div'); div.className = 'weiruan-toast'; div.innerText = msg;
            const colors = { success: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', error: 'linear-gradient(135deg, #ff6b6b 0%, #ee5a5a 100%)', info: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)', warning: 'linear-gradient(135deg, #f6d365 0%, #fda085 100%)' };
            div.style.cssText = `position: fixed; top: 80px; left: 50%; transform: translateX(-50%); background: ${colors[type] || colors.success}; color: ${type==='warning'?'#333':'white'}; padding: 12px 24px; border-radius: 8px; z-index: 2147483649; font-size: 14px; box-shadow: 0 4px 20px rgba(0,0,0,0.25); animation: weiruan-toast-in 0.3s ease-out; font-family: -apple-system, BlinkMacSystemFont, sans-serif; white-space: pre-line; line-height: 1.5;`;
            document.body.appendChild(div);
            setTimeout(() => { div.style.animation = 'weiruan-toast-out 0.3s ease-out forwards'; setTimeout(() => div.remove(), 300); }, 4500);
        },
        // 更新内存字典的公用方法
        updateMemoryMap: () => {
            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.file_id, obj); // 以 file_id 为 key,方便劫持时快速反查
                }
                for (let key in obj) {
                    if (key === 'return' || key === 'child' || key === 'sibling' || key.startsWith('__') || obj[key] instanceof Node) continue;
                    try { findFilesInFiber(obj[key], visited, depth + 1); } catch(e) {}
                }
            };
            if(document.body) {
                document.querySelectorAll('#root, #app, [class*="layout"], [class*="container"], [class*="body"]').forEach(el => {
                    const key = Object.keys(el).find(k => k.startsWith('__reactFiber$') || k.startsWith('__reactInternalInstance$'));
                    if (key) findFilesInFiber(el[key]);
                });
            }
        }
    };

    // ==================== 🕸️ 时空级底层网络劫持 ====================
    const NetworkHook = {
        init() {
            const win = unsafeWindow || window;
            if (win._weiruan_hooked) return;
            win._weiruan_hooked = true;

            Utils.log('🕸️ 威软拦截网已布署!正在监控底层发出的 get_download_url 请求');

            // 劫持批量下载、单个下载等关键接口
            const hookUrls = ['get_download_url', 'downloadUrl', 'batch'];

            const origFetch = win.fetch;
            win.fetch = async function(...args) {
                const reqUrl = typeof args[0] === 'string' ? args[0] : (args[0]?.url || '');
                const response = await origFetch.apply(this, args);
                
                if (hookUrls.some(u => reqUrl.includes(u))) {
                    try {
                        const clone = response.clone();
                        clone.json().then(data => {
                            NetworkHook.handleInterceptedData(args[1]?.body, data);
                        }).catch(()=>{});
                    } catch(e) {}
                }
                return response;
            };

            const origXhrSend = win.XMLHttpRequest.prototype.send;
            win.XMLHttpRequest.prototype.send = function(body) {
                this._weiruanBody = body;
                this.addEventListener('load', function() {
                    if (this.responseURL && hookUrls.some(u => this.responseURL.includes(u))) {
                        try {
                            const data = JSON.parse(this.responseText);
                            NetworkHook.handleInterceptedData(this._weiruanBody, data);
                        } catch(e) {}
                    }
                });
                return origXhrSend.call(this, body);
            };
        },

        // 处理劫持到的响应体
        handleInterceptedData(requestBodyStr, responseData) {
            Utils.updateMemoryMap(); // 确保有最新的内存字典
            let capturedFiles = [];

            // 提取请求中的 file_id (处理各种嵌套和 batch 批量请求的情况)
            let requestedFids = [];
            try {
                if (requestBodyStr && typeof requestBodyStr === 'string') {
                    const reqObj = JSON.parse(requestBodyStr);
                    if (reqObj.file_id) requestedFids.push(reqObj.file_id);
                    if (reqObj.requests) {
                        reqObj.requests.forEach(req => {
                            if (req.body && req.body.file_id) requestedFids.push(req.body.file_id);
                        });
                    }
                }
            } catch(e) {}

            // 解析阿里返回的数据结构 (单文件 or batch 批量数组)
            if (responseData.url && requestedFids.length > 0) {
                // 单文件直链
                const fid = requestedFids[0];
                const memInfo = State.fileMemoryMap.get(fid);
                capturedFiles.push({
                    fid: fid,
                    file_name: memInfo ? memInfo.name : `已劫持文件_${fid.substring(0,6)}.mp4`,
                    size: memInfo ? memInfo.size : 0,
                    download_url: responseData.url,
                    folderPath: '', fullPath: ''
                });
            } else if (responseData.responses && Array.isArray(responseData.responses)) {
                // 批量直链
                responseData.responses.forEach(res => {
                    if (res.body && res.body.url) {
                        // 尝试从请求体反推对应的 fid (阿里 batch 接口的 responses 通常和 requests 顺序一致,这里用 body 返回的 file_id 最准,如果没有就按顺序拿)
                        const fid = res.body.file_id || (requestedFids.length > 0 ? requestedFids.shift() : 'unknown');
                        const memInfo = State.fileMemoryMap.get(fid);
                        capturedFiles.push({
                            fid: fid,
                            file_name: memInfo ? memInfo.name : `已劫持文件_${fid.substring(0,6)}.mp4`,
                            size: memInfo ? memInfo.size : 0,
                            download_url: res.body.url,
                            folderPath: '', fullPath: ''
                        });
                    }
                });
            }

            // 只要抓到了真实直链,不管三七二十一,直接弹出威软面板!
            if (capturedFiles.length > 0 && document.body) {
                Utils.log('🎯 [无感劫持成功] 强行挂载提取面板!', capturedFiles);
                Utils.toast('🎉 威软引擎拦截成功!强制提取满速直链!', 'success');
                State.addHistory(capturedFiles);
                UI.showResultWindow(capturedFiles);
            }
        }
    };

    NetworkHook.init(); // 起手就埋下天罗地网

    // ==================== 界面 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-spin { to { transform: rotate(360deg); } }
                @keyframes weiruan-slide-in { from { opacity: 0; transform: scale(0.9); } to { opacity: 1; transform: scale(1); } }

                .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 18px; border: none; border-radius: 0 25px 25px 0; cursor: pointer; box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4); transition: all 0.3s ease; display: flex; align-items: center; gap: 6px; }
                .weiruan-btn:hover { padding-left: 22px; box-shadow: 0 6px 25px rgba(102, 126, 234, 0.5); }
                .weiruan-btn:disabled { opacity: 0.7; cursor: not-allowed; }
                .weiruan-icon { font-size: 16px; }

                .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: 8px 14px; border-radius: 20px; font-size: 12px; cursor: pointer; box-shadow: 0 2px 10px rgba(0,0,0,0.1); transition: all 0.2s; white-space: nowrap; }
                .weiruan-menu-item:hover { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; transform: translateX(5px); }

                /* Modal Styles */
                .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: var(--weiruan-bg, #ffffff); width: 720px; max-width: 92%; max-height: 85vh; border-radius: 16px; box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3); display: flex; flex-direction: column; overflow: hidden; animation: weiruan-slide-in 0.3s ease-out; font-family: -apple-system, BlinkMacSystemFont, sans-serif; }
                .weiruan-tab-content { display: none; flex-direction: column; min-height: 0; flex: 1; overflow: hidden; }
                .weiruan-tab-content.active { display: flex; }
                .weiruan-modal-header { padding: 18px 24px; border-bottom: 1px solid var(--weiruan-border, #eee); 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; display: flex; align-items: center; gap: 8px; }
                .weiruan-modal-close { cursor: pointer; font-size: 28px; line-height: 1; opacity: 0.8; transition: opacity 0.2s; width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: rgba(255,255,255,0.1); }
                .weiruan-modal-close:hover { opacity: 1; background: rgba(255,255,255,0.2); }
                .weiruan-toolbar { padding: 12px 24px; background: var(--weiruan-toolbar-bg, #f8f9ff); border-bottom: 1px solid var(--weiruan-border, #eee); display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px; }
                .weiruan-toolbar-info { font-size: 13px; color: var(--weiruan-text-secondary, #666); }
                .weiruan-btn-group { display: flex; gap: 6px; }
                .weiruan-action-btn { padding: 8px 16px; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 500; transition: all 0.2s; display: flex; align-items: center; gap: 4px; }
                .weiruan-action-btn.primary { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; }
                .weiruan-action-btn.success { background: linear-gradient(135deg, #56ab2f 0%, #a8e063 100%); color: white; }
                .weiruan-modal-body { padding: 16px 24px; overflow-y: auto; flex: 1; min-height: 0; max-height: 400px; background: var(--weiruan-bg, #ffffff); }
                .weiruan-file-item { background: var(--weiruan-item-bg, #f9f9f9); padding: 14px 16px; margin-bottom: 10px; border-radius: 10px; border-left: 4px solid #667eea; display: flex; justify-content: space-between; align-items: center; transition: all 0.2s; }
                .weiruan-file-info { overflow: hidden; flex: 1; margin-right: 12px; }
                .weiruan-file-name { font-weight: 600; color: var(--weiruan-text, #333); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: flex; align-items: center; gap: 6px; font-size: 14px; }
                .weiruan-file-meta { font-size: 12px; color: var(--weiruan-text-secondary, #888); margin-top: 4px; }
                .weiruan-file-actions { display: flex; gap: 6px; flex-shrink: 0; }
                .weiruan-file-btn { padding: 6px 12px; border: none; border-radius: 5px; cursor: pointer; font-size: 12px; font-weight: 500; transition: all 0.2s; text-decoration: none; display: inline-flex; align-items: center; gap: 3px; background: #333; color: white; }
                .weiruan-file-btn:hover { transform: scale(1.05); }
                .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-tabs { display: flex; border-bottom: 1px solid var(--weiruan-border, #eee); padding: 0 24px; background: var(--weiruan-bg, #ffffff); }
                .weiruan-tab { padding: 12px 20px; cursor: pointer; border: none; background: none; font-size: 14px; font-weight: 500; color: var(--weiruan-text-secondary, #666); position: relative; }
                .weiruan-tab.active { color: #667eea; }
                .weiruan-tab.active::after { content: ''; position: absolute; bottom: -1px; left: 0; right: 0; height: 2px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
                .weiruan-footer { padding: 12px 24px; border-top: 1px solid var(--weiruan-border, #eee); background: var(--weiruan-bg, #ffffff); text-align: center; font-size: 12px; color: var(--weiruan-text-secondary, #999); }
                .weiruan-dark { --weiruan-bg: #1a1a2e; --weiruan-text: #e0e0e0; --weiruan-text-secondary: #888; --weiruan-border: #333; --weiruan-item-bg: #252540; --weiruan-toolbar-bg: #1e1e35; }
            `);
        },
        applyTheme: () => {
            const modal = document.getElementById('weiruan-modal');
            if (modal) State.isDark() ? modal.classList.add('weiruan-dark') : modal.classList.remove('weiruan-dark');
        },
        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 = () => {
                Utils.toast(`🛡️ 由于阿里动态防盗链机制!\n\n请直接去勾选文件,然后点击页面上阿里云盘官方自带的【下载】按钮,\n助手会在底层为您瞬间截获满速直链!`, 'warning');
            };
            document.body.appendChild(btn);

            const menu = document.createElement('div');
            menu.className = 'weiruan-menu';
            menu.innerHTML = `<div class="weiruan-menu-item" data-action="starmap">🌌 星际历史图</div>`;
            menu.addEventListener('click', (e) => {
                if (e.target.getAttribute('data-action') === 'starmap') Utils.toast('星际历史图功能已就绪', 'info');
            });
            document.body.appendChild(menu);
        },
        showResultWindow: (data) => {
            const L = State.getLang();
            document.getElementById('weiruan-modal')?.remove();
            const totalSize = data.reduce((sum, f) => sum + f.size, 0);
            const modal = document.createElement('div');
            modal.id = 'weiruan-modal';
            modal.className = `weiruan-modal-overlay ${State.isDark() ? 'weiruan-dark' : ''}`;
            
            const activeUA = CONFIG.UA_ALIYUN;
            const batchLinks = Utils.generateBatchLinks(data);
            const aria2Commands = Utils.generateAria2Commands(data, activeUA);

            const fileListHTML = data.map((f, index) => `
                <div class="weiruan-file-item">
                    <div class="weiruan-file-info">
                        <div class="weiruan-file-name" title="${f.file_name}"><span>${Utils.getFileIcon(f.file_name)}</span><span>${f.file_name}</span></div>
                        <div class="weiruan-file-meta">${Utils.formatSize(f.size)} | 核心 ID: ${f.fid.substring(0, 8)}...</div>
                    </div>
                    <div class="weiruan-file-actions">
                        <a href="${f.download_url.replace(/"/g, '&quot;')}" target="_blank" class="weiruan-file-btn idm">⬇️ IDM / 浏览器</a>
                        <button class="weiruan-file-btn curl weiruan-copy-curl" data-index="${index}">📋 cURL</button>
                        <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"><span>🎉</span><span>拦截提取成功!(共 ${data.length} 个) - 阿里云盘</span></h3>
                    <span class="weiruan-modal-close" id="weiruan-modal-close">&times;</span>
                </div>
                <div class="weiruan-tabs"><button class="weiruan-tab active">📁 截获的文件</button></div>
                <div class="weiruan-tab-content active">
                    <div class="weiruan-toolbar">
                        <div class="weiruan-toolbar-info"><span>${L.totalSize}: <strong>${Utils.formatSize(totalSize)}</strong></span></div>
                        <div class="weiruan-btn-group">
                            <button class="weiruan-action-btn primary" id="weiruan-copy-all-btn">📦 复制全部直链</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">${L.title} v${CONFIG.VERSION} · 威软科技出品</div>
            </div>`;
            document.body.appendChild(modal);

            document.getElementById('weiruan-modal-close').addEventListener('click', () => modal.remove());
            modal.addEventListener('click', (e) => { if (e.target === modal) modal.remove(); });

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

            modal.querySelectorAll('.weiruan-copy-curl').forEach(btn => {
                btn.addEventListener('click', (e) => {
                    const f = data[parseInt(e.target.getAttribute('data-index'))];
                    GM_setClipboard(`curl -L -C - "${f.download_url}" -o "${f.file_name}" -A "${activeUA}" -b "${document.cookie}"`);
                    Utils.toast('✅ cURL 命令已复制');
                });
            });
            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(() => {
        UI.injectStyles();
        UI.createFloatButton();
        UI.applyTheme();
        console.log(`[威软全能助手] v${CONFIG.VERSION} 已在 DOM 挂载后启动`);
    });
})();