feat: add browser extension for GitHub download acceleration

This commit is contained in:
violettools
2026-01-23 14:58:33 +08:00
parent 6b75836fa4
commit 80b0a1ff61
11 changed files with 404 additions and 8 deletions

Submodule CloudflareSpeedTest deleted from 6eaacd6b2c

View File

@@ -0,0 +1,18 @@
# CFspider GitHub 加速扩展
CFspider 项目附带的浏览器扩展,用于加速 GitHub 文件下载。
## 安装
1. 打开 Chrome访问 `chrome://extensions/`
2. 开启「开发者模式」
3. 点击「加载已解压的扩展程序」
4. 选择此 `browser-extension` 文件夹
## 配置
点击扩展图标,填写 Workers 地址和 UUID保存即可。
## 使用
访问 GitHub Releases 页面,下载链接旁会显示「加速」按钮。

View File

@@ -0,0 +1,80 @@
/* CFspider GitHub 加速按钮样式 */
.cfspider-btn {
display: inline-flex;
align-items: center;
gap: 4px;
margin-left: 8px;
padding: 4px 10px;
background: linear-gradient(135deg, #00d4ff 0%, #7b2cbf 100%);
color: #fff !important;
font-size: 12px;
font-weight: 500;
border-radius: 6px;
text-decoration: none !important;
cursor: pointer;
transition: all 0.2s ease;
vertical-align: middle;
white-space: nowrap;
box-shadow: 0 2px 8px rgba(0, 212, 255, 0.3);
}
.cfspider-btn:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 212, 255, 0.4);
opacity: 0.95;
}
.cfspider-btn:active {
transform: translateY(0);
}
.cfspider-btn svg {
flex-shrink: 0;
}
.cfspider-btn.loading {
pointer-events: none;
opacity: 0.8;
}
.cfspider-btn .spinner {
animation: cfspider-spin 1s linear infinite;
}
@keyframes cfspider-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* GitHub Releases 页面特殊适配 */
.release-main-section .cfspider-btn {
margin-left: 12px;
}
/* Assets 列表适配 */
.Box-row .cfspider-btn {
margin-left: 8px;
}
/* 代码下载按钮适配 */
.get-repo-btn + .cfspider-btn,
.btn-primary + .cfspider-btn {
margin-left: 8px;
}
/* 暗色主题适配 */
[data-color-mode="dark"] .cfspider-btn,
.dark .cfspider-btn {
box-shadow: 0 2px 8px rgba(0, 212, 255, 0.2);
}
[data-color-mode="dark"] .cfspider-btn:hover,
.dark .cfspider-btn:hover {
box-shadow: 0 4px 12px rgba(0, 212, 255, 0.3);
}

View File

@@ -0,0 +1,189 @@
// CFspider GitHub 加速 - Content Script
(function() {
'use strict';
let config = { workersUrl: '', uuid: '', enabled: true };
// GitHub 下载链接匹配模式
const GITHUB_DOWNLOAD_PATTERNS = [
/https:\/\/github\.com\/[^\/]+\/[^\/]+\/releases\/download\/.+/,
/https:\/\/github\.com\/[^\/]+\/[^\/]+\/archive\/.+/,
/https:\/\/github\.com\/[^\/]+\/[^\/]+\/raw\/.+/,
/https:\/\/objects\.githubusercontent\.com\/.+/,
/https:\/\/raw\.githubusercontent\.com\/.+/,
/https:\/\/codeload\.github\.com\/.+/,
/https:\/\/github\.com\/[^\/]+\/[^\/]+\/suites\/\d+\/artifacts\/.+/
];
// 检查是否是 GitHub 下载链接
function isGitHubDownloadLink(url) {
return GITHUB_DOWNLOAD_PATTERNS.some(pattern => pattern.test(url));
}
// 生成加速链接
function generateAcceleratedUrl(originalUrl) {
if (!config.workersUrl || !config.uuid) return null;
// 构建代理 URL
const workersHost = config.workersUrl.replace(/^https?:\/\//, '').replace(/\/$/, '');
const encodedUrl = encodeURIComponent(originalUrl);
// 使用 /proxy API
return `https://${workersHost}/proxy?url=${encodedUrl}&method=GET`;
}
// 创建加速按钮
function createAccelerateButton(link) {
// 检查是否已添加按钮
if (link.nextElementSibling?.classList.contains('cfspider-btn')) return;
if (link.parentElement.querySelector('.cfspider-btn')) return;
const btn = document.createElement('a');
btn.className = 'cfspider-btn';
btn.innerHTML = `
<svg viewBox="0 0 16 16" width="14" height="14" fill="currentColor">
<path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm4.879-2.773 4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559V5.442a.25.25 0 0 1 .379-.215Z"/>
</svg>
加速
`;
btn.title = 'CFspider 加速下载';
btn.href = generateAcceleratedUrl(link.href) || '#';
btn.target = '_blank';
// 点击事件
btn.addEventListener('click', async (e) => {
e.preventDefault();
e.stopPropagation();
if (!config.workersUrl || !config.uuid) {
alert('请先在 CFspider 扩展中配置 Workers 地址和 UUID');
return;
}
// 开始下载
const acceleratedUrl = generateAcceleratedUrl(link.href);
if (acceleratedUrl) {
// 显示加载状态
btn.classList.add('loading');
btn.innerHTML = `
<svg class="spinner" viewBox="0 0 16 16" width="14" height="14" fill="currentColor">
<path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z" opacity="0.3"/>
<path d="M8 0a8 8 0 0 1 8 8h-1.5A6.5 6.5 0 0 0 8 1.5V0Z"/>
</svg>
加速中...
`;
try {
// 直接打开加速链接
window.open(acceleratedUrl, '_blank');
setTimeout(() => {
btn.classList.remove('loading');
btn.innerHTML = `
<svg viewBox="0 0 16 16" width="14" height="14" fill="currentColor">
<path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm4.879-2.773 4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559V5.442a.25.25 0 0 1 .379-.215Z"/>
</svg>
加速
`;
}, 1500);
} catch (err) {
console.error('CFspider 加速失败:', err);
btn.classList.remove('loading');
btn.innerHTML = '失败';
setTimeout(() => {
btn.innerHTML = `
<svg viewBox="0 0 16 16" width="14" height="14" fill="currentColor">
<path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm4.879-2.773 4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559V5.442a.25.25 0 0 1 .379-.215Z"/>
</svg>
加速
`;
}, 2000);
}
}
});
// 插入按钮
if (link.parentElement.classList.contains('d-flex') ||
link.closest('.Box-row') ||
link.closest('.release-main-section')) {
link.parentElement.insertBefore(btn, link.nextSibling);
} else {
link.insertAdjacentElement('afterend', btn);
}
}
// 扫描并处理下载链接
function scanDownloadLinks() {
if (!config.enabled) return;
const links = document.querySelectorAll('a[href]');
links.forEach(link => {
if (isGitHubDownloadLink(link.href)) {
createAccelerateButton(link);
}
});
}
// 加载配置
async function loadConfig() {
try {
const result = await chrome.storage.sync.get(['workersUrl', 'uuid', 'enabled']);
config = {
workersUrl: result.workersUrl || '',
uuid: result.uuid || '',
enabled: result.enabled !== false
};
} catch (e) {
console.error('CFspider: 加载配置失败', e);
}
}
// 监听配置更新
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'CONFIG_UPDATED') {
config = message.config;
scanDownloadLinks();
}
});
// 监听 DOM 变化
const observer = new MutationObserver((mutations) => {
let shouldScan = false;
mutations.forEach(mutation => {
if (mutation.addedNodes.length > 0) {
shouldScan = true;
}
});
if (shouldScan) {
setTimeout(scanDownloadLinks, 100);
}
});
// 初始化
async function init() {
await loadConfig();
scanDownloadLinks();
// 监听 DOM 变化
observer.observe(document.body, {
childList: true,
subtree: true
});
// 页面导航时重新扫描GitHub 使用 pjax
document.addEventListener('pjax:end', scanDownloadLinks);
document.addEventListener('turbo:load', scanDownloadLinks);
}
// 页面加载完成后初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();

View File

@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
<defs>
<linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#00d4ff"/>
<stop offset="100%" style="stop-color:#7b2cbf"/>
</linearGradient>
</defs>
<rect width="128" height="128" rx="24" fill="url(#grad)"/>
<text x="64" y="78" text-anchor="middle" font-family="Arial Black, sans-serif" font-size="48" font-weight="bold" fill="#fff">CF</text>
</svg>

After

Width:  |  Height:  |  Size: 485 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

View File

@@ -0,0 +1,39 @@
{
"manifest_version": 3,
"name": "CFspider GitHub 加速",
"version": "1.0.0",
"description": "使用 CFspider Workers 加速 GitHub 文件下载",
"author": "CFspider",
"homepage_url": "https://github.com/violettoolssite/CFspider",
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
},
"permissions": [
"storage"
],
"host_permissions": [
"https://github.com/*",
"https://objects.githubusercontent.com/*",
"https://raw.githubusercontent.com/*",
"https://codeload.github.com/*"
],
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icons/icon16.png",
"48": "icons/icon48.png"
}
},
"content_scripts": [
{
"matches": [
"https://github.com/*"
],
"js": ["content.js"],
"css": ["content.css"]
}
]
}

View File

@@ -0,0 +1,55 @@
// 加载配置
document.addEventListener('DOMContentLoaded', async () => {
const config = await chrome.storage.sync.get(['workersUrl', 'uuid', 'enabled']);
document.getElementById('workersUrl').value = config.workersUrl || '';
document.getElementById('uuid').value = config.uuid || '';
document.getElementById('enabled').checked = config.enabled !== false;
});
// 保存配置
document.getElementById('saveBtn').addEventListener('click', async () => {
const workersUrl = document.getElementById('workersUrl').value.trim();
const uuid = document.getElementById('uuid').value.trim();
const enabled = document.getElementById('enabled').checked;
const statusEl = document.getElementById('status');
// 验证
if (!workersUrl) {
statusEl.textContent = '请填写 Workers 地址';
statusEl.className = 'status error';
return;
}
if (!uuid) {
statusEl.textContent = '请填写 UUID';
statusEl.className = 'status error';
return;
}
// UUID 格式验证
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
if (!uuidRegex.test(uuid)) {
statusEl.textContent = 'UUID 格式不正确';
statusEl.className = 'status error';
return;
}
// 保存
await chrome.storage.sync.set({ workersUrl, uuid, enabled });
statusEl.textContent = '配置已保存';
statusEl.className = 'status success';
// 通知 content script 更新配置
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
if (tabs[0] && tabs[0].url.includes('github.com')) {
chrome.tabs.sendMessage(tabs[0].id, { type: 'CONFIG_UPDATED', config: { workersUrl, uuid, enabled } });
}
setTimeout(() => {
statusEl.className = 'status';
}, 2000);
});

19
test.py
View File

@@ -1,11 +1,16 @@
import cfspider
import os
# 直接使用海外代理
response = cfspider.get(
# 禁用系统代理
os.environ['NO_PROXY'] = '*'
os.environ['no_proxy'] = '*'
# VLESS 模式测试(需要 UUID
res = cfspider.get(
"https://httpbin.org/ip",
proxies={
"http": "http://us.cliproxy.io:3010",
"https": "http://2e75108689-region-JP:nf9ssu7a@us.cliproxy.io:3010"
}
cf_proxies="https://ip.kami666.xyz",
uuid="c373c80c-58e4-4e64-8db5-40096905ec58",
)
print(response.json())
print(f"状态码: {res.status_code}")
print(f"出口 IP: {res.text}")
print(f"CF 节点: {res.cf_colo}")