// ==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 = `
当前加速域名
${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 = `
${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 `
`;
}
// 选择加速源面板
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 在其他平台会自动回退。
`;
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(); }
})();