From 80b0a1ff61f4ec0f94042fcef457119dbca2d697 Mon Sep 17 00:00:00 2001 From: violettools Date: Fri, 23 Jan 2026 14:58:33 +0800 Subject: [PATCH] feat: add browser extension for GitHub download acceleration --- CloudflareSpeedTest | 1 - browser-extension/README.md | 18 +++ browser-extension/content.css | 80 ++++++++++++ browser-extension/content.js | 189 ++++++++++++++++++++++++++++ browser-extension/icons/icon.svg | 11 ++ browser-extension/icons/icon128.png | Bin 0 -> 2143 bytes browser-extension/icons/icon16.png | Bin 0 -> 291 bytes browser-extension/icons/icon48.png | Bin 0 -> 802 bytes browser-extension/manifest.json | 39 ++++++ browser-extension/popup.js | 55 ++++++++ test.py | 19 +-- 11 files changed, 404 insertions(+), 8 deletions(-) delete mode 160000 CloudflareSpeedTest create mode 100644 browser-extension/README.md create mode 100644 browser-extension/content.css create mode 100644 browser-extension/content.js create mode 100644 browser-extension/icons/icon.svg create mode 100644 browser-extension/icons/icon128.png create mode 100644 browser-extension/icons/icon16.png create mode 100644 browser-extension/icons/icon48.png create mode 100644 browser-extension/manifest.json create mode 100644 browser-extension/popup.js diff --git a/CloudflareSpeedTest b/CloudflareSpeedTest deleted file mode 160000 index 6eaacd6..0000000 --- a/CloudflareSpeedTest +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6eaacd6b2ca37fd43fbb9a9d5d89c94ff96dc162 diff --git a/browser-extension/README.md b/browser-extension/README.md new file mode 100644 index 0000000..6be49f0 --- /dev/null +++ b/browser-extension/README.md @@ -0,0 +1,18 @@ +# CFspider GitHub 加速扩展 + +CFspider 项目附带的浏览器扩展,用于加速 GitHub 文件下载。 + +## 安装 + +1. 打开 Chrome,访问 `chrome://extensions/` +2. 开启「开发者模式」 +3. 点击「加载已解压的扩展程序」 +4. 选择此 `browser-extension` 文件夹 + +## 配置 + +点击扩展图标,填写 Workers 地址和 UUID,保存即可。 + +## 使用 + +访问 GitHub Releases 页面,下载链接旁会显示「加速」按钮。 diff --git a/browser-extension/content.css b/browser-extension/content.css new file mode 100644 index 0000000..796a9f5 --- /dev/null +++ b/browser-extension/content.css @@ -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); +} + diff --git a/browser-extension/content.js b/browser-extension/content.js new file mode 100644 index 0000000..efeb5f1 --- /dev/null +++ b/browser-extension/content.js @@ -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 = ` + + + + 加速 + `; + 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 = ` + + + + + 加速中... + `; + + try { + // 直接打开加速链接 + window.open(acceleratedUrl, '_blank'); + + setTimeout(() => { + btn.classList.remove('loading'); + btn.innerHTML = ` + + + + 加速 + `; + }, 1500); + } catch (err) { + console.error('CFspider 加速失败:', err); + btn.classList.remove('loading'); + btn.innerHTML = '失败'; + setTimeout(() => { + btn.innerHTML = ` + + + + 加速 + `; + }, 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(); + } +})(); + diff --git a/browser-extension/icons/icon.svg b/browser-extension/icons/icon.svg new file mode 100644 index 0000000..5160d06 --- /dev/null +++ b/browser-extension/icons/icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + CF + + diff --git a/browser-extension/icons/icon128.png b/browser-extension/icons/icon128.png new file mode 100644 index 0000000000000000000000000000000000000000..759300bd6a634a56929f7a549cbaef834f8f8f56 GIT binary patch literal 2143 zcmb8xX*|@68VB(I3}ek$LpWI?BV#nK!#HGXA_v(+*~TcLqGTUTwro+^T^WTT`_i!+ z%P?aYge=K2*~b}6APX8v)?zHOSCRWTkI>EYas$|7jF0x!vrhih1NPhAFy*P0bOoTK;~bf|P!Q^1 z2$Sbf24OI?`OVF!Je^%Zh{P$PE}w@K?F7OopFhkK!VUpFhZX?oi0Nltp~WV!Dh)bV zFQF!6`*X-ZdG)14@hC0aNeQ1Puuuc^U_kL?u)s6OeCb_>7DXn*Ltvc~5DD^x^&&n{e)ql2^Xx{#aS`^4NCgTYx7KQ}-dAu(=tr8MadZ0{_OP28 z3)8uBS5uhix=Z*bLS%EP=vKZR#5G^bwfK^rn6$7UCmS87!k%tdc1mH)SAmjz6Vt9;%^0F}#jSWh*yhA?>yR|@n$W!qx(SCS_VOtu`;b~9R=`Kh zENzTxBY23J?e@-_oPOG2q$cUSC6Vav@5>cP-9dIH`5#YvjlZ!xV;YHyvgf~nI8gLQ ziI6k&g1!_MlKb}uW)N*U)oSh0+5)B!hqYa|@np5-FwUz;_#x>k@uyjW{HvyrRpgIR zX)DsaTBU&4A+MTls&I`N@G`=c;L1G?w#fiU%UJ zu{nUf?OAun=A*K(`@TSjq~9{3|K}GgtxQWm!pqdzCW(m=&)8UaR^!+-eMWY?&RE?5;51AQGr4 z+4*d}HqFJkUGtvQasL2{0T>RW8Iyv=tM!YplrP{s#|jq~GljC8rbqb-E?u3zOaGpu z1k*3HzcRE`PUwRdpzGB?pKAy@I$4{MAg3ywPgI!+6(7olER|Ux^EMw#V^u*_Kb+t|I>cVsme^E!G$?UJ&!ErwVqq?thKp{`G0giSljU59h2> z|FRe}|OZgILv$#qtS0Y4^(dFeyMSiIo8H?q; z0c))|;>_zOHqJ;GHUVhqPAxCvu_}0X_VO!l3q-Ez6>K0(MOhV3joOWF0o+wx2=1w* zQqh*)sQwvk30!|}kAqr*MaoFk?{#RP)&#p~74UU+eQaQ9)45(?{QCZ|Wmb{zI$xUa zdCaHXDNTMTEK;|m@4@=qvE4s@Umn7o-TI@vLs$VW?6!*fT<5A&-<_`XX6kVF+x?E6 zD^{W;tF#OlhM7pWiR37_B_U)}H!L>pUH$RNcB}K$2Pd}byDj^4Qo%b(YgSY-n6;M5 zP#M&y8RMOudScMdI>4Z}J6tNZbn8^aDMtv>5hSykRy8-PtodM_!lsST;p3;n#v8(N z;s?rjMjF~M^!Hn?=*E|~Bd+8Jo`6a)&A^4P@(&M|vl}*&nto~1s1`cCB8*KV0?s3k zXW-ZchjZa0IoK-S9%42%R!VZJv7ig`b#?NJ95ItD)TQX+Fai08+`a_6So;;b{&t++ ze(asJ($0HQBWR(shs|}99}e%FulN==@}hfvfgJgb5zhOU+;l=kB`xtd^sQ3Qk$>5p z%K`C<0a>=8%gWB^hK`a-$*rCDzGoaO2aXyPV^$`hpTI-#o}bYJ5$j6~pT1Cx%l8C| zWl&>)WQ)DNNMRyHc(+44**BCmKR5sWh=)5ifYxpo6xW#|_5FG;rkUH||9 literal 0 HcmV?d00001 diff --git a/browser-extension/icons/icon16.png b/browser-extension/icons/icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..52cde2a4bdf1250f16a2199a94f2b63561548b8a GIT binary patch literal 291 zcmV+;0o?wHP)Ov?6u)r9uBAg6?65I?XN1lNBv-Lz7w!i)cX6Igg!!YH+N31Sjgau-^rZB^< z*WVa+zxj@$0OW$@hT?F(?B0hAPk#Ktbiu#>3=Axc;HU)yQ8s3V-~ay;bO95LvHQ(8 zhH6zo1_e$Q1~E2fh9w4K47$8*xWW$8fIV-%gS~LZMuy>rgB-)5cRv^ozxzSZ0LI~v pkbEGyiY(xx!U%BzIfg*>0005wO)k6d6dC{k002ovPDHLkV1nGacpv}( literal 0 HcmV?d00001 diff --git a/browser-extension/icons/icon48.png b/browser-extension/icons/icon48.png new file mode 100644 index 0000000000000000000000000000000000000000..aaddab7402645165a30721ccb565bbe957de37e4 GIT binary patch literal 802 zcmV+-1Ks?IP)2{vT4BxzB*WW%U1o`wS2`hrl@m&LMCP zfpZ9)BW~tR)iyIOmN{_3V?()Bgi9g>V=6v|qIl|`$0J`DOX|w?Vj%(Y$tuI2(J?%S zE1##+>wsAhaHp;i*QyKHW2`1}M2^EJ$5CMwanx;P$JMF=v<>+2DI7(n5sey>06aff zjN={~W@0LCeVxI>u@GcsInpgID?0bNQNKgNtHu%>cr%7kIlkszE&UUC9gx%FfZ-mv zQSD^n15pLdedFjHnOh-VqcVXytOi)*G!wh@gORa%o~ z!;!TOxfyyJOLVWd4f^rSzmUERNU1`eS5)L0q7fslF*Tg#^epZzAETqGSzck;)Z)%4 zWW?y@bTV3vP7AE2_2*)~NcefA65S0&sI!}qbq8KdMG%Us$Rh!l${g$3Unq63#y_{) z&UQi89avHmY&mJ%t#j!{RTJJ`n_PMid|^zb`X^J+MxGAe1s73hCiERH!{ze)73HLz zwHDj)wy6{@$%G$~7_NT_Ak&CO4daOfj=i72 { + 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); +}); + diff --git a/test.py b/test.py index c3cafc9..4f04e5e 100644 --- a/test.py +++ b/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()) \ No newline at end of file +print(f"状态码: {res.status_code}") +print(f"出口 IP: {res.text}") +print(f"CF 节点: {res.cf_colo}")