mirror of
https://github.com/violettoolssite/CFspider.git
synced 2026-04-04 18:59:02 +08:00
feat: add browser extension for GitHub download acceleration
This commit is contained in:
Submodule CloudflareSpeedTest deleted from 6eaacd6b2c
18
browser-extension/README.md
Normal file
18
browser-extension/README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# CFspider GitHub 加速扩展
|
||||
|
||||
CFspider 项目附带的浏览器扩展,用于加速 GitHub 文件下载。
|
||||
|
||||
## 安装
|
||||
|
||||
1. 打开 Chrome,访问 `chrome://extensions/`
|
||||
2. 开启「开发者模式」
|
||||
3. 点击「加载已解压的扩展程序」
|
||||
4. 选择此 `browser-extension` 文件夹
|
||||
|
||||
## 配置
|
||||
|
||||
点击扩展图标,填写 Workers 地址和 UUID,保存即可。
|
||||
|
||||
## 使用
|
||||
|
||||
访问 GitHub Releases 页面,下载链接旁会显示「加速」按钮。
|
||||
80
browser-extension/content.css
Normal file
80
browser-extension/content.css
Normal 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);
|
||||
}
|
||||
|
||||
189
browser-extension/content.js
Normal file
189
browser-extension/content.js
Normal 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();
|
||||
}
|
||||
})();
|
||||
|
||||
11
browser-extension/icons/icon.svg
Normal file
11
browser-extension/icons/icon.svg
Normal 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 |
BIN
browser-extension/icons/icon128.png
Normal file
BIN
browser-extension/icons/icon128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
BIN
browser-extension/icons/icon16.png
Normal file
BIN
browser-extension/icons/icon16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 291 B |
BIN
browser-extension/icons/icon48.png
Normal file
BIN
browser-extension/icons/icon48.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 802 B |
39
browser-extension/manifest.json
Normal file
39
browser-extension/manifest.json
Normal 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"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
55
browser-extension/popup.js
Normal file
55
browser-extension/popup.js
Normal 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
19
test.py
@@ -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}")
|
||||
|
||||
Reference in New Issue
Block a user