// ==UserScript== // @name GitHub Xget 下载加速器 - 增强优化版 // @namespace http://tampermonkey.net/ // @version 3.9 // @description 自动加速 GitHub、GitLab、Gitea 等平台的文件下载,支持多平台和自定义加速域名,增强版功能 | UP:毕加索自画像 // @author Xget | Enhanced & Optimized by 毕加索自画像 // @match https://github.com/* // @match https://gist.github.com/* // @match https://gitlab.com/* // @match https://gitea.com/* // @match https://codeberg.org/* // @match https://sourceforge.net/* // @match https://android.googlesource.com/* // @match https://huggingface.co/* // @match https://civitai.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=github.com // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @grant GM_xmlhttpRequest // @connect * // @license MIT // ==/UserScript== (function() { 'use strict'; // [诊断] 修改页面标题 try { document.title = '⚡XGET| ' + document.title; } catch(e) {} // 预设加速源列表 const PRESET_ACCELERATORS = [ { domain: 'xget.xi-xu.me', name: '⭐ Xget 官方', type: 'xget', testPath: '/', platforms: 'all', description: '全平台 | 官方实例' }, { domain: 'gh-proxy.com', name: '⚡ GH-Proxy 官方', type: 'ghproxy', testPath: '/', platforms: ['github.com', 'raw.githubusercontent.com', 'gist.github.com', 'codeload.github.com', 'objects.githubusercontent.com'], description: '仅 GitHub | 节点多、带宽大' }, { domain: 'ghproxy.net', name: '🚀 GHProxy 专线', type: 'ghproxy', testPath: '/', platforms: ['github.com', 'raw.githubusercontent.com', 'gist.github.com', 'codeload.github.com', 'objects.githubusercontent.com'], description: '仅 GitHub | 国内链路优化' }, { domain: 'github.akams.cn', name: '💎 AKAMS 开发者源', type: 'ghproxy', testPath: '/', platforms: ['github.com', 'raw.githubusercontent.com', 'gist.github.com', 'codeload.github.com', 'objects.githubusercontent.com'], description: '仅 GitHub | 大资源支持好' }, { domain: 'gh.llkk.cc', name: '🔹 LLKK 节点', type: 'ghproxy', testPath: '/', platforms: ['github.com', 'raw.githubusercontent.com', 'gist.github.com', 'codeload.github.com', 'objects.githubusercontent.com'], description: '仅 GitHub | 相对稳定' }, { domain: 'github.moeyy.xyz', name: '🔸 Moeyy 萌丫云', type: 'ghproxy', testPath: '/', platforms: ['github.com', 'raw.githubusercontent.com', 'gist.github.com', 'codeload.github.com', 'objects.githubusercontent.com'], description: '仅 GitHub | 知名度高、延迟低' } ]; // 自建部署链接 const SELF_HOST_LINKS = { xget: 'https://github.com/xixu-me/xget', ghproxy: 'https://github.com/hunshcn/gh-proxy' }; // 安全读取 GM 存储 function safeGMGet(key, defaultValue) { try { return GM_getValue(key, defaultValue); } catch (e) { console.warn('[Xget] GM_getValue 读取失败,使用默认值:', key, e); return defaultValue; } } // 配置项 const CONFIG = { defaultDomain: 'xget.xi-xu.me', defaultType: 'xget', enabled: safeGMGet('xget_enabled', true), customDomain: safeGMGet('xget_custom_domain', ''), customType: safeGMGet('xget_custom_type', 'xget'), userAccelerators: safeGMGet('xget_user_accelerators', []), showNotification: safeGMGet('xget_show_notification', true), stats: safeGMGet('xget_stats', { total: 0, success: 0, failed: 0 }), whitelistMode: safeGMGet('xget_whitelist_mode', false), excludeList: safeGMGet('xget_exclude_list', []), autoCheck: safeGMGet('xget_auto_check', true), serverStatus: safeGMGet('xget_server_status', { available: true, lastCheck: 0 }), debug: safeGMGet('xget_debug', false), maxRetries: safeGMGet('xget_max_retries', 2), strongIntercept: safeGMGet('xget_strong_intercept', true), failoverEnabled: safeGMGet('xget_failover_enabled', true), degradedUntil: safeGMGet('xget_degraded_until', 0) }; let allServerStatus = {}; const DOWNLOAD_EXTENSIONS = [ 'zip', 'tar', 'gz', 'bz2', '7z', 'rar', 'xz', 'tgz', 'tar.xz', 'tar.bz2', 'exe', 'dmg', 'deb', 'rpm', 'msi', 'pkg', 'apk', 'appimage', 'bin', 'safetensors', 'pt', 'pth', 'ckpt', 'h5', 'onnx', 'pb', 'model', 'gguf', 'ggml', 'mlmodel', 'tflite', 'engine', 'trt', 'iso', 'img', 'jar', 'war' ]; const PLATFORM_CONFIG = { 'github.com': { prefix: 'gh', name: 'GitHub', patterns: [/\/releases\/download\//, /\/archive\/.*\.(zip|tar\.gz|tar)$/, /\/raw\//, /\/.*\/.*\/.*\.(exe|dmg|deb|rpm|msi|pkg|apk|zip|tar\.gz|tar\.bz2|7z|rar)$/] }, 'codeload.github.com': { prefix: 'gh', name: 'GitHub', patterns: [/\/zip\//, /\/tar\.gz\//, /\/tar\//] }, 'raw.githubusercontent.com': { prefix: 'gh', name: 'GitHub', patterns: [/.*/] }, 'objects.githubusercontent.com': { prefix: 'gh', name: 'GitHub', patterns: [/.*/] }, 'gist.github.com': { prefix: 'gist', name: 'GitHub Gist', patterns: [/\/raw\//, /\/download/] }, 'gitlab.com': { prefix: 'gl', name: 'GitLab', patterns: [/\/-\/archive\//, /\/-\/project\/.*\/uploads\//, /\/uploads\//] }, 'gitea.com': { prefix: 'gitea', name: 'Gitea', patterns: [/\/archive\//, /\/raw\//, /\/releases\/download\//] }, 'codeberg.org': { prefix: 'codeberg', name: 'Codeberg', patterns: [/\/archive\//, /\/raw\//, /\/releases\/download\//] }, 'sourceforge.net': { prefix: 'sf', name: 'SourceForge', patterns: [/\/files\//, /\/downloads\//] }, 'android.googlesource.com': { prefix: 'aosp', name: 'AOSP', patterns: [/\/\+archive\//] }, 'huggingface.co': { prefix: 'hf', name: 'Hugging Face', patterns: [/\/resolve\//, /\/.*\/.*\/(blob|resolve)\/.*\.(bin|safetensors|pt|pth|ckpt|h5|onnx|pb|model)$/] }, 'civitai.com': { prefix: 'civitai', name: 'Civitai', patterns: [/\/api\/download\//] } }; function debugLog(...args) { if (CONFIG.debug) console.log('[Xget Debug]', new Date().toLocaleTimeString(), ...args); } function throttle(func, limit) { let inThrottle; return function(...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => { inThrottle = false; }, limit); } }; } function debounce(func, delay) { let timeoutId; return function(...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => func.apply(this, args), delay); }; } const saveStatsDebounced = debounce(() => { try { GM_setValue('xget_stats', CONFIG.stats); } catch (e) { console.warn('[Xget] 保存统计数据失败:', e); } }, 600); // 指示器内容更新 function renderIndicator() { const el = CONFIG._indicatorEl; if (!el) return; const domain = getAcceleratorDomain(); const type = getAcceleratorType(); const isAvailable = CONFIG.serverStatus.available; const isDegraded = CONFIG.degradedUntil > Date.now(); const statusText = isDegraded ? '降级中' : (isAvailable ? '可用' : '不可用'); const accelerator = getAcceleratorByDomain(domain); const platformInfo = accelerator?.platforms === 'all' ? '全平台' : '仅 GitHub'; el.style.background = isDegraded ? 'rgba(245, 158, 11, 0.95)' : (isAvailable ? 'rgba(39, 122, 113, 0.95)' : 'rgba(220, 38, 38, 0.95)'); el.innerHTML = `
${domain}
${type}·${statusText}·${platformInfo}
`; } function getAcceleratorDomain() { return CONFIG.customDomain || CONFIG.defaultDomain; } function getAcceleratorType() { return CONFIG.customType || CONFIG.defaultType; } function getAllAccelerators() { return [...PRESET_ACCELERATORS, ...CONFIG.userAccelerators]; } function getAcceleratorByDomain(domain) { return getAllAccelerators().find(a => a.domain === domain); } function getCurrentPlatform() { return PLATFORM_CONFIG[window.location.hostname]; } function compileExcludeMatchers(list) { const matchers = []; for (const raw of (list || [])) { const pattern = String(raw || '').trim(); if (!pattern) continue; if (pattern.startsWith('/') && pattern.lastIndexOf('/') > 0) { const lastSlash = pattern.lastIndexOf('/'); const body = pattern.slice(1, lastSlash); const flags = pattern.slice(lastSlash + 1); try { matchers.push({ type: 'regex', raw: pattern, re: new RegExp(body, flags) }); continue; } catch (e) { debugLog('排除规则正则解析失败:', pattern, e); } } try { matchers.push({ type: 'regex', raw: pattern, re: new RegExp(pattern) }); } catch { matchers.push({ type: 'substr', raw: pattern, s: pattern }); } } return matchers; } function refreshExcludeMatchers() { CONFIG._excludeMatchers = compileExcludeMatchers(CONFIG.excludeList); } function isExcluded(url) { return (CONFIG._excludeMatchers || []).some(m => m.type === 'regex' ? m.re.test(url) : url.includes(m.s)); } function hasDownloadableExtension(pathname) { const lowerPath = pathname.toLowerCase(); return DOWNLOAD_EXTENSIONS.some(ext => lowerPath.endsWith('.' + ext)); } function isDownloadLink(url, element) { try { const urlObj = new URL(url); const platform = PLATFORM_CONFIG[urlObj.hostname]; if (!platform) return false; if (isExcluded(url)) return false; if (element && (element.download || element.hasAttribute('download'))) return true; return platform.patterns.some(pattern => pattern.test(urlObj.pathname)) || hasDownloadableExtension(urlObj.pathname); } catch (e) { return false; } } function isAcceleratorSupportPlatform(accelerator, hostname) { if (!accelerator || accelerator.platforms === 'all') return true; return Array.isArray(accelerator.platforms) && accelerator.platforms.includes(hostname); } function convertToAcceleratorURL(originalUrl) { try { const url = new URL(originalUrl); const platform = PLATFORM_CONFIG[url.hostname]; if (!platform) return originalUrl; const domain = getAcceleratorDomain(); const type = getAcceleratorType(); const acc = getAcceleratorByDomain(domain); if (!isAcceleratorSupportPlatform(acc, url.hostname)) { const official = PRESET_ACCELERATORS.find(a => a.domain === 'xget.xi-xu.me'); if (official) return `https://${official.domain}/${platform.prefix}${url.pathname + url.search + url.hash}`; return originalUrl; } return type === 'ghproxy' ? `https://${domain}/${originalUrl}` : `https://${domain}/${platform.prefix}${url.pathname + url.search + url.hash}`; } catch (e) { return originalUrl; } } function convertWithAccelerator(originalUrl, acceleratorDomain, acceleratorType) { try { const url = new URL(originalUrl); const platform = PLATFORM_CONFIG[url.hostname]; if (!platform) return null; const acc = getAcceleratorByDomain(acceleratorDomain); if (!isAcceleratorSupportPlatform(acc, url.hostname)) return null; return acceleratorType === 'ghproxy' ? `https://${acceleratorDomain}/${originalUrl}` : `https://${acceleratorDomain}/${platform.prefix}${url.pathname + url.search + url.hash}`; } catch (e) { return null; } } async function convertWithFailover(originalUrl) { const currentDomain = getAcceleratorDomain(); const currentType = getAcceleratorType(); const primaryResult = convertWithAccelerator(originalUrl, currentDomain, currentType); if (primaryResult && (!CONFIG.autoCheck || CONFIG.serverStatus.available)) { return { url: primaryResult, domain: currentDomain, fallback: false }; } if (CONFIG.failoverEnabled) { for (const acc of getAllAccelerators()) { if (acc.domain === currentDomain) continue; const result = convertWithAccelerator(originalUrl, acc.domain, acc.type); if (!result) continue; const accStatus = allServerStatus[acc.domain]; if (accStatus && !accStatus.available) continue; if (await checkAcceleratorAvailable(acc)) { return { url: result, domain: acc.domain, fallback: true }; } } } if (currentDomain !== 'xget.xi-xu.me') { const xgetResult = convertWithAccelerator(originalUrl, 'xget.xi-xu.me', 'xget'); if (xgetResult && await checkAcceleratorAvailable(PRESET_ACCELERATORS.find(a => a.domain === 'xget.xi-xu.me'))) { return { url: xgetResult, domain: 'xget.xi-xu.me', fallback: true }; } } return null; } function checkAcceleratorAvailable(accelerator) { if (CONFIG.degradedUntil > Date.now()) return Promise.resolve(false); const cached = allServerStatus[accelerator.domain]; if (cached && (Date.now() - cached.lastCheck < 5 * 60 * 1000)) return Promise.resolve(cached.available); return new Promise((resolve) => { const timeout = setTimeout(() => { allServerStatus[accelerator.domain] = { available: false, latency: -1, lastCheck: Date.now() }; resolve(false); }, 2500); GM_xmlhttpRequest({ method: 'GET', url: `https://${accelerator.domain}${accelerator.testPath}`, timeout: 2500, onload: function(res) { clearTimeout(timeout); const available = res.status >= 200 && res.status < 400; allServerStatus[accelerator.domain] = { available, latency: Date.now(), lastCheck: Date.now() }; resolve(available); }, onerror: function() { clearTimeout(timeout); resolve(false); }, ontimeout: function() { clearTimeout(timeout); resolve(false); } }); }); } async function checkServerAvailability() { if (Date.now() - CONFIG.serverStatus.lastCheck < 5 * 60 * 1000) return CONFIG.serverStatus.available; try { const domain = getAcceleratorDomain(); const accelerator = getAcceleratorByDomain(domain); const testPath = accelerator?.testPath || '/'; return new Promise((resolve) => { const timeout = setTimeout(() => { updateServerStatus(false); resolve(false); }, 3000); GM_xmlhttpRequest({ method: 'GET', url: `https://${domain}${testPath}`, timeout: 3000, onload: function(res) { clearTimeout(timeout); const available = (res.status >= 200 && res.status < 400); updateServerStatus(available); resolve(available); }, onerror: function() { clearTimeout(timeout); updateServerStatus(false); resolve(false); }, ontimeout: function() { clearTimeout(timeout); updateServerStatus(false); resolve(false); } }); }); } catch (e) { updateServerStatus(false); return false; } } function updateServerStatus(available) { CONFIG.serverStatus = { available, lastCheck: Date.now() }; GM_setValue('xget_server_status', CONFIG.serverStatus); renderIndicator(); } function updateStats(success) { CONFIG.stats.total++; if (success) CONFIG.stats.success++; else CONFIG.stats.failed++; saveStatsDebounced(); renderIndicator(); } // 显示通知 function showNotification(message, type = 'info', duration = 3000) { if (!CONFIG.showNotification) return; const colors = { success: '#10b981', info: '#3b82f6', warning: '#f59e0b', error: '#ef4444' }; const notification = document.createElement('div'); notification.style.cssText = ` position: fixed; top: 20px; right: 20px; padding: 15px 20px; background: ${colors[type] || colors.info}; color: white; border-radius: 8px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); z-index: 10002; font-size: 14px; max-width: 300px; animation: xgetSlideIn 0.3s ease-out; cursor: pointer; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; `; notification.textContent = message; notification.addEventListener('click', () => { notification.style.animation = 'xgetSlideIn 0.3s ease-out reverse'; setTimeout(() => notification.remove(), 300); }); if (!document.getElementById('xget-notification-style')) { const style = document.createElement('style'); style.id = 'xget-notification-style'; style.textContent = `@keyframes xgetSlideIn { from { transform: translateX(400px); opacity: 0; } to { transform: translateX(0); opacity: 1; } }`; document.head.appendChild(style); } document.body.appendChild(notification); setTimeout(() => { if (notification.parentNode) notification.remove(); }, duration); } function interceptDownloadLinks() { const platform = getCurrentPlatform(); if (!platform || !CONFIG.enabled) return; const handleClick = throttle(async function(e) { if (e.button !== 0 || e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) return; const target = e.target.closest('a[href]'); if (!target?.href || !isDownloadLink(target.href, target)) return; if (CONFIG.degradedUntil > Date.now()) return; e.preventDefault(); e.stopPropagation(); if (CONFIG.strongIntercept) e.stopImmediatePropagation(); const result = await convertWithFailover(target.href); if (result) { if (result.fallback) { showNotification(`🔄 已自动路由至备份线路: ${result.domain}`, 'info', 2500); } else { showNotification(`🚀 已启用 ${platform.name} 独占加速`, 'success', 2000); } updateStats(true); const link = document.createElement('a'); link.href = result.url; link.download = target.download || ''; link.target = '_blank'; link.rel = 'noopener noreferrer'; document.body.appendChild(link); link.click(); document.body.removeChild(link); } else { CONFIG.degradedUntil = Date.now() + 2 * 60 * 1000; GM_setValue('xget_degraded_until', CONFIG.degradedUntil); updateServerStatus(false); showNotification('⚠️ 所有加速源拥堵,回退到原始服务器链接下载', 'warning'); updateStats(false); window.open(target.href, '_blank', 'noopener,noreferrer'); } }, 300); document.addEventListener('click', handleClick, true); } // 添加页面指示器 function addPageIndicator() { if (!getCurrentPlatform() || !CONFIG.enabled) return; const indicator = document.createElement('div'); indicator.id = 'xget-indicator'; indicator.style.cssText = ` position: fixed; bottom: 24px; right: 24px; padding: 10px 14px; color: white; border-radius: 12px; font-size: 12px; z-index: 9999; box-shadow: 0 10px 30px -5px rgba(0,0,0,0.25); cursor: pointer; transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; min-width: 170px; user-select: none; backdrop-filter: blur(8px); border: 1px solid rgba(255,255,255,0.1); `; CONFIG._indicatorEl = indicator; renderIndicator(); indicator.addEventListener('mouseenter', () => { indicator.style.transform = 'scale(1.04)'; }); indicator.addEventListener('mouseleave', () => { indicator.style.transform = 'scale(1)'; }); indicator.addEventListener('click', () => { const stats = CONFIG.stats; const successRate = stats.total > 0 ? ((stats.success / stats.total) * 100).toFixed(1) : 0; showNotification( `主网关: ${getAcceleratorDomain()}\n调度流水: ${stats.total} | 成功: ${stats.success} | 降级: ${stats.failed}\n分配就绪率: ${successRate}%`, 'info', 5000 ); }); document.body.appendChild(indicator); if (typeof MutationObserver !== 'undefined') { const observer = new MutationObserver(() => { if (CONFIG.enabled && CONFIG._indicatorEl && !document.body.contains(CONFIG._indicatorEl)) { document.body.appendChild(CONFIG._indicatorEl); } }); observer.observe(document.body, { childList: true }); } } // 设置菜单命令 function setupMenuCommands() { GM_registerMenuCommand(CONFIG.enabled ? '❌ 禁用加速' : '✅ 启用加速', function() { CONFIG.enabled = !CONFIG.enabled; GM_setValue('xget_enabled', CONFIG.enabled); showNotification(CONFIG.enabled ? '加速已启用' : '加速已禁用', 'success'); location.reload(); }); GM_registerMenuCommand('⚙️ 设置加速域名', showAcceleratorSelector); GM_registerMenuCommand('📊 查看统计', showStatsPanel); GM_registerMenuCommand('🚫 管理排除列表', showExcludeListPanel); GM_registerMenuCommand('🔍 检测服务器状态', async function() { showNotification('正在检测服务器状态...', 'info', 2000); CONFIG.serverStatus.lastCheck = 0; const available = await checkServerAvailability(); showNotification(available ? '✅ 加速服务器可用' : '❌ 加速服务器不可用', available ? 'success' : 'error', 3000); }); GM_registerMenuCommand('🧪 测试转换', showTestPanel); GM_registerMenuCommand('🔁 高级设置', showAdvancedSettingsPanel); } // 通用模态框创建函数 function createModal(id, title, content, options = {}) { const existing = document.getElementById(id); if (existing) existing.remove(); const overlay = document.createElement('div'); overlay.id = id; overlay.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); z-index: 10001; display: flex; justify-content: center; align-items: center; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; animation: xgetFadeIn 0.2s lighten; `; const panel = document.createElement('div'); panel.style.cssText = ` background: white; border-radius: 16px; padding: 24px; max-width: ${options.maxWidth || '450px'}; width: 90%; max-height: 80vh; overflow-y: auto; box-shadow: 0 25px 80px rgba(0, 0, 0, 0.35); animation: xgetSlideUp 0.3s ease-out; `; panel.innerHTML = `

${title}

${content}
`; if (!document.getElementById('xget-modal-animations')) { const style = document.createElement('style'); style.id = 'xget-modal-animations'; style.textContent = ` @keyframes xgetFadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes xgetSlideUp { from { opacity: 0; transform: translateY(20px) scale(0.95); } to { opacity: 1; transform: translateY(0) scale(1); } } @keyframes xgetSlideOut { from { opacity: 1; transform: translateY(0) scale(1); } to { opacity: 0; transform: translateY(20px) scale(0.95); } } `; document.head.appendChild(style); } overlay.appendChild(panel); document.body.appendChild(overlay); const closeModal = () => { panel.style.animation = 'xgetSlideOut 0.2s ease-out forwards'; setTimeout(() => overlay.remove(), 200); }; panel.querySelector('.xget-modal-close').addEventListener('click', closeModal); overlay.addEventListener('click', (e) => { if (e.target === overlay) closeModal(); }); return { overlay, panel, close: closeModal }; } // 统计面板 function showStatsPanel() { const stats = CONFIG.stats; const successRate = stats.total > 0 ? ((stats.success / stats.total) * 100).toFixed(1) : 0; const domain = getAcceleratorDomain(); const content = `
${stats.total}
总下载
${stats.success}
成功
${stats.failed}
失败
成功率 ${successRate}%
当前加速域名 ${domain}
服务器状态 ${CONFIG.serverStatus.available ? '✅ 可用' : '❌ 不可用'}
调试模式 ${CONFIG.debug ? '开启' : '关闭'}
🎨 增强优化版 UP:毕加索自画像
`; const { panel, close } = createModal('xget-stats-panel', '📊 加速统计', content); panel.querySelector('#xget-reset-stats-btn').addEventListener('click', () => { if (confirm('确定要重置所有统计数据吗?')) { CONFIG.stats = { total: 0, success: 0, failed: 0 }; GM_setValue('xget_stats', CONFIG.stats); showNotification('统计数据已重置', 'success'); close(); } }); } // 排除列表面板 function showExcludeListPanel() { const content = `
💡 每行一个规则,支持正则表达式(如 /test/)或普通文本匹配
`; const { panel, close } = createModal('xget-exclude-panel', '🚫 排除列表', content); panel.querySelector('#xget-exclude-save-btn').addEventListener('click', () => { const newList = panel.querySelector('#xget-exclude-textarea').value.split('\n').filter(x => x.trim()); CONFIG.excludeList = newList; GM_setValue('xget_exclude_list', newList); refreshExcludeMatchers(); showNotification(`已保存 ${newList.length} 条排除规则`, 'success'); close(); }); panel.querySelector('#xget-exclude-clear-btn').addEventListener('click', () => { panel.querySelector('#xget-exclude-textarea').value = ''; }); } // 测试转换面板 function showTestPanel() { const content = `
`; const { panel } = createModal('xget-test-panel', '🧪 测试 URL 转换', content); panel.querySelector('#xget-test-btn').addEventListener('click', () => { const testUrl = panel.querySelector('#xget-test-input').value.trim(); if (!testUrl) return; const converted = convertToAcceleratorURL(testUrl); const isDownload = isDownloadLink(testUrl, null); panel.querySelector('#xget-test-original').textContent = testUrl; panel.querySelector('#xget-test-converted').textContent = converted; panel.querySelector('#xget-test-isdownload').innerHTML = isDownload ? '✅ 是' : '❌ 否'; panel.querySelector('#xget-test-type').textContent = getAcceleratorType(); panel.querySelector('#xget-test-result').style.display = 'block'; }); } // 高级设置面板 function showAdvancedSettingsPanel() { const content = `
重试次数
URL 转换失败时的重试次数
${CONFIG.maxRetries}
${createToggleOption('xget-notification-toggle', '显示通知', '下载加速时显示提示通知', CONFIG.showNotification)} ${createToggleOption('xget-autocheck-toggle', '自动检测服务器', '下载前检测加速服务器可用性', CONFIG.autoCheck)} ${createToggleOption('xget-debug-toggle', '调试模式', '在控制台输出详细日志', CONFIG.debug)} ${createToggleOption('xget-failover-toggle', '故障自动切换', '当前源不可用时自动尝试其他源', CONFIG.failoverEnabled)} ${createToggleOption('xget-strong-toggle', '强力拦截模式', '阻止其他脚本干扰下载拦截', CONFIG.strongIntercept)}
`; const { panel, close } = createModal('xget-advanced-panel', '🔧 高级设置', content); let retryValue = CONFIG.maxRetries; panel.querySelectorAll('.xget-retry-btn').forEach(btn => { btn.addEventListener('click', () => { if (btn.dataset.action === 'plus' && retryValue < 5) retryValue++; if (btn.dataset.action === 'minus' && retryValue > 0) retryValue--; panel.querySelector('#xget-retry-value').textContent = retryValue; }); }); panel.querySelector('#xget-save-advanced-btn').addEventListener('click', () => { CONFIG.maxRetries = retryValue; CONFIG.showNotification = panel.querySelector('#xget-notification-toggle').checked; CONFIG.autoCheck = panel.querySelector('#xget-autocheck-toggle').checked; CONFIG.debug = panel.querySelector('#xget-debug-toggle').checked; CONFIG.strongIntercept = panel.querySelector('#xget-strong-toggle').checked; CONFIG.failoverEnabled = panel.querySelector('#xget-failover-toggle').checked; GM_setValue('xget_max_retries', CONFIG.maxRetries); GM_setValue('xget_show_notification', CONFIG.showNotification); GM_setValue('xget_auto_check', CONFIG.autoCheck); GM_setValue('xget_debug', CONFIG.debug); GM_setValue('xget_strong_intercept', CONFIG.strongIntercept); GM_setValue('xget_failover_enabled', CONFIG.failoverEnabled); showNotification('设置已保存', 'success'); close(); }); } function createToggleOption(id, title, desc, isChecked) { return `
${title}
${desc}
`; } // 选择加速源面板 function showAcceleratorSelector() { const existing = document.getElementById('xget-accelerator-selector'); if (existing) existing.remove(); const currentDomain = getAcceleratorDomain(); const overlay = document.createElement('div'); overlay.id = 'xget-accelerator-selector'; overlay.style.cssText = `position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); z-index: 10001; display: flex; justify-content: center; align-items: center; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;`; const panel = document.createElement('div'); panel.style.cssText = `background: white; border-radius: 12px; padding: 24px; max-width: 500px; width: 90%; max-height: 80vh; overflow-y: auto; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);`; panel.innerHTML = `

⚙️ 选择加速源

当前使用: ${currentDomain}
💡 提示:全平台 支持所有平台;仅 GitHub 在其他平台会自动回退。
📡 预设加速源
👤 自定义加速源
🚀 推荐自建加速服务
⭐ Xget (全平台) gh-proxy (仅GitHub)
`; overlay.appendChild(panel); document.body.appendChild(overlay); const presetList = panel.querySelector('#xget-preset-list'); PRESET_ACCELERATORS.forEach(acc => { presetList.appendChild(createAcceleratorItem(acc, currentDomain, false)); }); const customList = panel.querySelector('#xget-custom-list'); if (CONFIG.userAccelerators.length === 0) customList.innerHTML = '
暂无自定义加速源
'; else CONFIG.userAccelerators.forEach(acc => { customList.appendChild(createAcceleratorItem(acc, currentDomain, true)); }); panel.querySelector('#xget-close-btn').addEventListener('click', () => overlay.remove()); overlay.addEventListener('click', (e) => { if (e.target === overlay) overlay.remove(); }); panel.querySelector('#xget-add-custom-btn').addEventListener('click', () => { showAddCustomAcceleratorDialog(); }); panel.querySelector('#xget-test-all-btn').addEventListener('click', async () => { const btn = panel.querySelector('#xget-test-all-btn'); btn.disabled = true; btn.textContent = '⏳ 测试中...'; const results = await testAllAccelerators(); const resultDiv = panel.querySelector('#xget-test-result'); resultDiv.style.display = 'block'; const avCount = results.filter(r => r.available).length; const fastest = results.filter(r => r.available).sort((a, b) => a.latency - b.latency)[0]; if (avCount > 0) { resultDiv.style.background = '#d1fae5'; resultDiv.style.color = '#065f46'; resultDiv.innerHTML = `✅ ${avCount}/${results.length} 个可用 · 最快: ${fastest?.domain} (${fastest?.latency}ms)`; } else { resultDiv.style.background = '#fee2e2'; resultDiv.style.color = '#991b1b'; resultDiv.innerHTML = `❌ 所有加速源均不可用`; } btn.disabled = false; btn.textContent = '🔍 测试所有加速源'; updateAcceleratorListStatus(results); }); } function createAcceleratorItem(acc, currentDomain, isCustom) { const item = document.createElement('div'); const isSelected = acc.domain === currentDomain; const platformInfo = acc.platforms === 'all' ? '全平台' : (acc.description || '仅 GitHub'); item.style.cssText = `display: flex; align-items: center; justify-content: space-between; padding: 12px; margin-bottom: 8px; background: ${isSelected ? '#ecfdf5' : '#f9fafb'}; border: 2px solid ${isSelected ? '#10b981' : 'transparent'}; border-radius: 8px; cursor: pointer;`; item.dataset.domain = acc.domain; item.innerHTML = `
${acc.name} ${isSelected ? '✓' : ''}
${acc.domain}
${acc.type} ${platformInfo}
${isCustom ? '' : ''}
`; item.addEventListener('click', (e) => { if (e.target.closest('.xget-delete-btn')) return; selectAccelerator(acc); }); if (isCustom) { item.querySelector('.xget-delete-btn').addEventListener('click', (e) => { e.stopPropagation(); if (confirm(`确定要删除加速源 "${acc.name}" 吗?`)) deleteCustomAccelerator(acc.domain); }); } return item; } function selectAccelerator(acc) { CONFIG.customDomain = acc.domain; CONFIG.customType = acc.type; GM_setValue('xget_custom_domain', acc.domain); GM_setValue('xget_custom_type', acc.type); GM_setValue('xget_server_status', { available: true, lastCheck: 0 }); let message = `已切换到 ${acc.name}`; if (acc.platforms !== 'all') { message += `\n⚠️ 该加速源仅支持 GitHub,其他平台将自动回退到 Xget 官方`; showNotification(message, 'warning', 4000); } else { showNotification(message, 'success'); } const selector = document.getElementById('xget-accelerator-selector'); if (selector) selector.remove(); setTimeout(() => location.reload(), 600); } // 添加自定义加速源对话框 function showAddCustomAcceleratorDialog() { const content = `
`; const { panel, close } = createModal('xget-cacc-panel', '➕ 添加自定义加速源', content); const setupRadioGroup = (name) => { const labels = panel.querySelectorAll(`input[name="${name}"]`); labels.forEach(r => { r.parentElement.addEventListener('click', () => { labels.forEach(x => { x.checked = false; x.parentElement.style.borderColor = '#e5e7eb'; }); r.checked = true; r.parentElement.style.borderColor = '#10b981'; }); animateRadioTransition(r); }); }; setupRadioGroup('xget-cacc-type'); setupRadioGroup('xget-cacc-platforms'); panel.querySelector('#xget-cacc-save-btn').addEventListener('click', () => { const err = panel.querySelector('#xget-cacc-error'); const name = panel.querySelector('#xget-cacc-name').value.trim(); const dom = panel.querySelector('#xget-cacc-domain').value.trim().replace(/^https?:\/\//, '').replace(/\/$/, ''); const type = panel.querySelector('input[name="xget-cacc-type"]:checked').value; const plat = panel.querySelector('input[name="xget-cacc-platforms"]:checked').value; if (!name || !dom) { err.textContent = '请输入完整数据'; err.style.display = 'block'; return; } const testPath = '/'; const platforms = plat === 'all' ? 'all' : ['github.com', 'raw.githubusercontent.com', 'gist.github.com', 'codeload.github.com', 'objects.githubusercontent.com']; const newAcc = { domain: dom, name, type, testPath, platforms, description: plat === 'all' ? '支持所有平台' : '仅支持 GitHub' }; CONFIG.userAccelerators.push(newAcc); GM_setValue('xget_user_accelerators', CONFIG.userAccelerators); showNotification(`已添加: ${name}`, 'success'); close(); const selector = document.getElementById('xget-accelerator-selector'); if (selector) selector.remove(); setTimeout(showAcceleratorSelector, 200); }); } function animateRadioTransition(radio) { if (radio.checked) { radio.parentElement.style.borderColor = '#10b981'; } } function deleteCustomAccelerator(domain) { CONFIG.userAccelerators = CONFIG.userAccelerators.filter(a => a.domain !== domain); GM_setValue('xget_user_accelerators', CONFIG.userAccelerators); if (CONFIG.customDomain === domain) { CONFIG.customDomain = ''; CONFIG.customType = 'xget'; GM_setValue('xget_custom_domain', ''); GM_setValue('xget_custom_type', 'xget'); } showNotification('已删除加速源', 'success'); showAcceleratorSelector(); } // 批量测速函数 async function testAllAccelerators() { const results = []; for (const acc of getAllAccelerators()) { const startTime = Date.now(); const available = await new Promise((resolve) => { const timeout = setTimeout(() => resolve(false), 5000); GM_xmlhttpRequest({ method: 'GET', url: `https://${acc.domain}${acc.testPath}`, timeout: 5000, onload: (res) => { clearTimeout(timeout); resolve(res.status >= 200 && res.status < 400); }, onerror: () => { clearTimeout(timeout); resolve(false); }, ontimeout: () => { clearTimeout(timeout); resolve(false); } }); }); const latency = Date.now() - startTime; allServerStatus[acc.domain] = { available, latency, lastCheck: Date.now() }; results.push({ domain: acc.domain, available, latency }); } return results; } function updateAcceleratorListStatus(results) { results.forEach(result => { const item = document.querySelector(`[data-domain="${result.domain}"]`); if (item) { const indicator = item.querySelector('.xget-status-indicator'); const latencyEl = item.querySelector('.xget-latency'); if (indicator) indicator.style.background = result.available ? '#10b981' : '#ef4444'; if (latencyEl) { if (result.available && result.latency > 0) { latencyEl.textContent = result.latency + 'ms'; latencyEl.style.color = result.latency < 500 ? '#10b981' : (result.latency < 1500 ? '#f59e0b' : '#ef4444'); } else { latencyEl.textContent = '超时'; latencyEl.style.color = '#ef4444'; } } } }); } function showConsoleBanner() { console.log('%c⚡ Xget 加加速器增强优化版', 'color: #10b981; font-size: 16px; font-weight: bold;'); console.log('%c🎨 UP:毕加索自画像', 'color: #3b82f6; font-size: 12px;'); console.log('%c✨ 全量加速源可用性机制校准完毕 | v3.9', 'color: #6b7280; font-size: 11px;'); } function setupEasterEgg() { let seq = []; const secret = ['x', 'g', 'e', 't']; document.addEventListener('keydown', (e) => { seq.push(e.key.toLowerCase()); if (seq.length > secret.length) seq.shift(); if (seq.join('') === secret.join('')) { showAuthorInfo(); seq = []; } }); } function showAuthorInfo() { const modal = document.createElement('div'); modal.style.cssText = `position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; border-radius: 16px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); z-index: 10003; text-align: center; font-family: sans-serif;`; modal.innerHTML = `
🎨
Xget 加速器增强优化版
v3.9
毕加索自画像
性能、稳定性、错误处理已优化
`; modal.addEventListener('click', () => modal.remove()); document.body.appendChild(modal); } function init() { showConsoleBanner(); setupEasterEgg(); refreshExcludeMatchers(); setupMenuCommands(); if (CONFIG.enabled) { interceptDownloadLinks(); setTimeout(addPageIndicator, 1000); } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); setTimeout(init, 5000); } else { init(); } })();