diff --git a/PRIVACY_POLICY.md b/PRIVACY_POLICY.md index 5692093..84b1be0 100644 --- a/PRIVACY_POLICY.md +++ b/PRIVACY_POLICY.md @@ -1,10 +1,10 @@ # 隐私政策 -## 生效日期:2025 年 7 月 12 日 +## 生效日期:2025 年 8 月 8 日 ## 概述 -Xget for Chromium 是一个开源浏览器扩展,旨在通过 Xget 代理服务重定向下载链接来加速 GitHub、GitLab 和 Hugging Face 的下载。我们承诺保护您的隐私并确保数据处理的透明度。 +Xget for Chromium 是一个开源浏览器扩展,旨在通过 Xget 代理服务重定向下载链接来加速众多软件开发和研究平台的下载,包括 GitHub、GitLab、Hugging Face、npm、PyPI、Maven、RubyGems、CRAN 等 20+ 个平台。我们承诺保护您的隐私并确保数据处理的透明度。 ## 我们的隐私承诺 @@ -35,7 +35,7 @@ Xget for Chromium 扩展遵循“隐私优先”的设计原则,所有处理 - **Xget 域名配置**:您设置的自定义 Xget 服务域名 - **扩展启用状态**:扩展是否启用的开关状态 -- **平台偏好设置**:为 GitHub、GitLab 和 Hugging Face 启用/禁用的选择 +- **平台偏好设置**:为支持的各个平台(GitHub、GitLab、Hugging Face、npm、PyPI、Maven、RubyGems、CRAN 等)启用/禁用的选择 这些设置: @@ -84,9 +84,11 @@ Xget for Chromium 扩展遵循“隐私优先”的设计原则,所有处理 该扩展在以下平台上运行: -- GitHub (github.com) -- GitLab (gitlab.com) -- Hugging Face (huggingface.co) +- **代码托管平台**:GitHub、GitLab、Gitea、Codeberg、SourceForge、Android Source +- **包管理平台**:npm、PyPI、Anaconda、Maven Central、Gradle Plugin Portal、RubyGems、CRAN、CPAN、CTAN、Go Proxy、NuGet、Crates.io、Packagist +- **AI/ML 平台**:Hugging Face +- **学术平台**:arXiv +- **应用分发**:F-Droid 扩展不会与这些平台共享任何数据,也不会修改您与这些平台的交互,除了重定向下载链接。 @@ -99,14 +101,14 @@ Xget for Chromium 扩展遵循“隐私优先”的设计原则,所有处理 1. **`downloads`**:检测和管理下载以进行重定向 2. **`storage`**:在本地存储您的扩展设置 3. **`activeTab`**:在当前标签页中显示通知 -4. **`host_permissions`**:访问受支持平台以检测下载链接 +4. **`host_permissions`**:访问受支持的 20+ 个平台以检测下载链接 ### 权限使用 - **下载权限**:仅用于检测下载并将其重定向到 Xget - **存储权限**:仅用于保存您的扩展设置 - **标签页权限**:仅用于显示下载重定向通知 -- **主机权限**:仅用于在受支持平台上检测下载链接 +- **主机权限**:仅用于在受支持的各个平台上检测下载链接 我们不使用这些权限来收集数据、跟踪您的活动或任何其他目的。 @@ -210,7 +212,7 @@ Xget for Chromium 扩展遵循“隐私优先”的设计原则,所有处理 --- -**最后更新**:2025 年 7 月 18 日 +**最后更新**:2025 年 8 月 8 日 **联系方式**:[GitHub Issues](https://github.com/xixu-me/Xget-for-Chromium/issues) 此隐私政策是我们对透明度和用户隐私承诺的一部分。我们相信您有权了解您使用的软件如何处理您的数据,以及在这种情况下,确认它不会处理您的个人数据。 diff --git a/README.md b/README.md index b8b90fd..1bddc80 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,10 @@ # Xget for Chromium -一个强大的 Chromium 浏览器扩展,通过自动将下载重定向到 [Xget](https://github.com/xixu-me/Xget) 加速引擎,从而加速 GitHub、GitLab 和 Hugging Face 的下载。 +为 Chromium 浏览器提供的 [Xget](https://github.com/xixu-me/Xget) 下载加速扩展,支持 GitHub、GitLab、npm、PyPI 等众多平台的文件下载加速。 ## 🚀 功能特性 - **🎯 自动下载加速**:无缝重定向下载通过 Xget 获得更快速度 -- **🌐 多平台支持**:支持 GitHub、GitLab 和 Hugging Face - **⚙️ 可配置设置**:自定义你的 Xget 域名和平台偏好 - **🔔 智能通知**:下载重定向时的可视化反馈 - **🛡️ 隐私优先**:所有处理都在你的浏览器本地进行 @@ -70,29 +69,15 @@ - 输入你的 Xget 域名(例如 `xget.xi-xu.me`) - 启用扩展 -2. **选择平台**(可选) +2. **选择平台** - 根据需要切换各个平台的开关 - - 默认启用所有平台(GitHub、GitLab、Hugging Face) + - 默认启用所有平台 3. **开始下载** - 访问任何受支持的平台 - 像往常一样点击下载链接 - 下载将自动通过 Xget 加速 -## 🎯 支持的平台 - -| 平台 | 状态 | URL 模式 | 扩展 ID | -|----------|---------|-------------|--------------| -| **GitHub** | ✅ 支持 | `github.com/*` | `gh` | -| **GitLab** | ✅ 支持 | `gitlab.com/*` | `gl` | -| **Hugging Face** | ✅ 支持 | `huggingface.co/*` | `hf` | - -### 支持的下载类型 - -- **GitHub**:发布资源、存储库存档、原始文件、Git LFS 文件 -- **GitLab**:项目存档、发布下载、存储库导出 -- **Hugging Face**:模型文件、数据集文件、模型卡片、分词器 - ## 🔧 工作原理 1. **检测**:扩展监控受支持平台上的下载链接 @@ -100,19 +85,6 @@ 3. **重定向**:下载通过你配置的 Xget 域名路由 4. **加速**:Xget 的全球 CDN 和优化功能提供更快的下载速度 -### URL 转换示例 - -```text -原始: https://github.com/user/repo/archive/main.zip -Xget: https://xget.xi-xu.me/gh/user/repo/archive/main.zip - -原始: https://gitlab.com/user/repo/-/archive/main.zip -Xget: https://xget.xi-xu.me/gl/user/repo/-/archive/main.zip - -原始: https://huggingface.co/user/model/resolve/main/model.bin -Xget: https://xget.xi-xu.me/hf/user/model/resolve/main/model.bin -``` - ### 为什么 Xget 让下载更快 - **边缘缓存**:文件在 Cloudflare 的全球边缘位置进行缓存 @@ -146,14 +118,13 @@ Xget: https://xget.xi-xu.me/hf/user/model/resolve/main/model.bin - **Chrome**:版本 88+(Manifest V3 支持) - **Xget 服务**:访问 Xget 实例 - 使用公共实例:`xget.xi-xu.me` - - 或部署你自己的:[Xget 存储库](https://github.com/xixu-me/Xget)(推荐) + - 或部署你自己的:[Xget 存储库](https://github.com/xixu-me/Xget) ### 关于 Xget 服务 Xget 是基于 Cloudflare Workers 构建的高性能、安全的代理服务,提供: - **全球边缘分发**:通过 Cloudflare 的全球网络加快下载速度 -- **多平台支持**:针对 GitHub、GitLab 和 Hugging Face 进行优化 - **高级功能**:HTTP/3 支持、智能缓存、自动重试 - **安全性**:全面的安全标头和内容保护 - **可靠性**:内置超时保护和性能监控 diff --git a/background.js b/background.js index 863e291..ddb96b7 100644 --- a/background.js +++ b/background.js @@ -1,33 +1,202 @@ -// 与 worker.js 匹配的平台配置 +// 平台配置 const PLATFORMS = { + // 代码托管平台 gh: { base: "https://github.com", name: "GitHub", - transform: (path) => path.replace(/^\/gh\//, "/"), pattern: /^https:\/\/github\.com\//, }, gl: { base: "https://gitlab.com", name: "GitLab", - transform: (path) => path.replace(/^\/gl\//, "/"), pattern: /^https:\/\/gitlab\.com\//, }, + gitea: { + base: "https://gitea.com", + name: "Gitea", + pattern: /^https:\/\/gitea\.com\//, + }, + codeberg: { + base: "https://codeberg.org", + name: "Codeberg", + pattern: /^https:\/\/codeberg\.org\//, + }, + sf: { + base: "https://sourceforge.net", + name: "SourceForge", + pattern: /^https:\/\/sourceforge\.net\//, + }, + aosp: { + base: "https://android.googlesource.com", + name: "AOSP", + pattern: /^https:\/\/android\.googlesource\.com\//, + }, + + // AI/ML 平台 hf: { base: "https://huggingface.co", name: "Hugging Face", - transform: (path) => path.replace(/^\/hf\//, "/"), pattern: /^https:\/\/huggingface\.co\//, }, + + // 包管理平台 + npm: { + base: "https://registry.npmjs.org", + name: "npm", + pattern: /^https:\/\/registry\.npmjs\.org\//, + }, + pypi: { + base: "https://pypi.org", + name: "PyPI", + pattern: /^https:\/\/pypi\.org\//, + }, + "pypi-files": { + base: "https://files.pythonhosted.org", + name: "PyPI Files", + pattern: /^https:\/\/files\.pythonhosted\.org\//, + }, + conda: { + base: "https://repo.anaconda.com", + name: "Conda", + pattern: /^https:\/\/repo\.anaconda\.com\//, + }, + "conda-community": { + base: "https://conda.anaconda.org", + name: "Conda Community", + pattern: /^https:\/\/conda\.anaconda\.org\//, + }, + maven: { + base: "https://repo1.maven.org", + name: "Maven", + pattern: /^https:\/\/repo1\.maven\.org\//, + }, + apache: { + base: "https://downloads.apache.org", + name: "Apache", + pattern: /^https:\/\/downloads\.apache\.org\//, + }, + gradle: { + base: "https://plugins.gradle.org", + name: "Gradle", + pattern: /^https:\/\/plugins\.gradle\.org\//, + }, + rubygems: { + base: "https://rubygems.org", + name: "RubyGems", + pattern: /^https:\/\/rubygems\.org\//, + }, + cran: { + base: "https://cran.r-project.org", + name: "CRAN", + pattern: /^https:\/\/cran\.r-project\.org\//, + }, + cpan: { + base: "https://www.cpan.org", + name: "CPAN", + pattern: /^https:\/\/www\.cpan\.org\//, + }, + ctan: { + base: "https://tug.ctan.org", + name: "CTAN", + pattern: /^https:\/\/tug\.ctan\.org\//, + }, + golang: { + base: "https://proxy.golang.org", + name: "Go Modules", + pattern: /^https:\/\/proxy\.golang\.org\//, + }, + nuget: { + base: "https://api.nuget.org", + name: "NuGet", + pattern: /^https:\/\/api\.nuget\.org\//, + }, + crates: { + base: "https://crates.io", + name: "Crates.io", + pattern: /^https:\/\/crates\.io\//, + }, + packagist: { + base: "https://repo.packagist.org", + name: "Packagist", + pattern: /^https:\/\/repo\.packagist\.org\//, + }, + + // 其他平台 + arxiv: { + base: "https://arxiv.org", + name: "arXiv", + pattern: /^https:\/\/arxiv\.org\//, + }, + fdroid: { + base: "https://f-droid.org", + name: "F-Droid", + pattern: /^https:\/\/f-droid\.org\//, + }, }; -// 默认设置 +// 转换URL为Xget格式 +function transformUrl(url, xgetDomain, enabledPlatforms) { + try { + const platform = detectPlatform(url); + if (!platform || !enabledPlatforms[platform]) { + return null; + } + + const urlObj = new URL(url); + const path = urlObj.pathname + urlObj.search + urlObj.hash; + + return `https://${xgetDomain}/${platform}${path}`; + } catch (error) { + console.error("转换 URL 时出错:", error); + return null; + } +} + +// 检测平台 +function detectPlatform(url) { + for (const [key, platform] of Object.entries(PLATFORMS)) { + if (platform.pattern.test(url)) { + return key; + } + } + return null; +} const DEFAULT_SETTINGS = { enabled: true, xgetDomain: "xget.xi-xu.me", enabledPlatforms: { + // 代码托管平台 gh: true, gl: true, + gitea: true, + codeberg: true, + sf: true, + aosp: true, + + // AI/ML 平台 hf: true, + + // 包管理平台 + npm: true, + pypi: true, + "pypi-files": true, + conda: true, + "conda-community": true, + maven: true, + apache: true, + gradle: true, + rubygems: true, + cran: true, + cpan: true, + ctan: true, + golang: true, + nuget: true, + crates: true, + packagist: true, + + // 其他平台 + arxiv: true, + fdroid: true, }, }; @@ -56,7 +225,11 @@ async function handleDownload(downloadItem, suggest) { } const url = downloadItem.url; - const redirectedUrl = transformUrl(url, settings); + const redirectedUrl = transformUrl( + url, + settings.xgetDomain, + settings.enabledPlatforms + ); if (redirectedUrl && redirectedUrl !== url) { console.log("重定向下载:", url, "->", redirectedUrl); @@ -89,7 +262,7 @@ async function handleDownload(downloadItem, suggest) { } } -function transformUrl(url, settings) { +function transformUrlLegacy(url, settings) { try { // 找到匹配的平台 for (const [platformKey, platform] of Object.entries(PLATFORMS)) { @@ -122,11 +295,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { // 通知相关标签页刷新 try { const tabs = await chrome.tabs.query({ - url: [ - "https://github.com/*", - "https://gitlab.com/*", - "https://huggingface.co/*", - ], + url: Object.values(PLATFORMS).map((platform) => platform.base + "/*"), }); for (const tab of tabs) { diff --git a/content.js b/content.js index fbc52fc..6c0a618 100644 --- a/content.js +++ b/content.js @@ -1,7 +1,8 @@ // 内容脚本,在支持的平台上注入下载拦截功能 -// 平台检测和 URL 转换 +// 平台配置 const PLATFORMS = { + // 代码托管平台 gh: { base: "https://github.com", name: "GitHub", @@ -12,11 +13,127 @@ const PLATFORMS = { name: "GitLab", pattern: /^https:\/\/gitlab\.com\//, }, + gitea: { + base: "https://gitea.com", + name: "Gitea", + pattern: /^https:\/\/gitea\.com\//, + }, + codeberg: { + base: "https://codeberg.org", + name: "Codeberg", + pattern: /^https:\/\/codeberg\.org\//, + }, + sf: { + base: "https://sourceforge.net", + name: "SourceForge", + pattern: /^https:\/\/sourceforge\.net\//, + }, + aosp: { + base: "https://android.googlesource.com", + name: "AOSP", + pattern: /^https:\/\/android\.googlesource\.com\//, + }, + + // AI/ML 平台 hf: { base: "https://huggingface.co", name: "Hugging Face", pattern: /^https:\/\/huggingface\.co\//, }, + + // 包管理平台 + npm: { + base: "https://registry.npmjs.org", + name: "npm", + pattern: /^https:\/\/registry\.npmjs\.org\//, + }, + pypi: { + base: "https://pypi.org", + name: "PyPI", + pattern: /^https:\/\/pypi\.org\//, + }, + "pypi-files": { + base: "https://files.pythonhosted.org", + name: "PyPI Files", + pattern: /^https:\/\/files\.pythonhosted\.org\//, + }, + conda: { + base: "https://repo.anaconda.com", + name: "Conda", + pattern: /^https:\/\/repo\.anaconda\.com\//, + }, + "conda-community": { + base: "https://conda.anaconda.org", + name: "Conda Community", + pattern: /^https:\/\/conda\.anaconda\.org\//, + }, + maven: { + base: "https://repo1.maven.org", + name: "Maven", + pattern: /^https:\/\/repo1\.maven\.org\//, + }, + apache: { + base: "https://downloads.apache.org", + name: "Apache", + pattern: /^https:\/\/downloads\.apache\.org\//, + }, + gradle: { + base: "https://plugins.gradle.org", + name: "Gradle", + pattern: /^https:\/\/plugins\.gradle\.org\//, + }, + rubygems: { + base: "https://rubygems.org", + name: "RubyGems", + pattern: /^https:\/\/rubygems\.org\//, + }, + cran: { + base: "https://cran.r-project.org", + name: "CRAN", + pattern: /^https:\/\/cran\.r-project\.org\//, + }, + cpan: { + base: "https://www.cpan.org", + name: "CPAN", + pattern: /^https:\/\/www\.cpan\.org\//, + }, + ctan: { + base: "https://tug.ctan.org", + name: "CTAN", + pattern: /^https:\/\/tug\.ctan\.org\//, + }, + golang: { + base: "https://proxy.golang.org", + name: "Go Modules", + pattern: /^https:\/\/proxy\.golang\.org\//, + }, + nuget: { + base: "https://api.nuget.org", + name: "NuGet", + pattern: /^https:\/\/api\.nuget\.org\//, + }, + crates: { + base: "https://crates.io", + name: "Crates.io", + pattern: /^https:\/\/crates\.io\//, + }, + packagist: { + base: "https://repo.packagist.org", + name: "Packagist", + pattern: /^https:\/\/repo\.packagist\.org\//, + }, + + // 其他平台 + arxiv: { + base: "https://arxiv.org", + name: "arXiv", + pattern: /^https:\/\/arxiv\.org\//, + }, + fdroid: { + base: "https://f-droid.org", + name: "F-Droid", + pattern: /^https:\/\/f-droid\.org\//, + }, }; // 初始化内容脚本 @@ -221,7 +338,101 @@ function isDownloadLink(link) { console.error("无效的 URL:", href, e); } - // 第七检查:明确的下载文本指示器(更具体) + // 第六检查:npm 包下载 + const allowedNpmHosts = ["registry.npmjs.org"]; + try { + const parsedUrl = new URL(href); + if (allowedNpmHosts.includes(parsedUrl.host)) { + // npm tarball URL 包含 /-/ + if (pathname.includes("/-/") && pathname.endsWith(".tgz")) { + return true; + } + } + } catch (e) { + console.error("无效的 URL:", href, e); + } + + // 第七检查:PyPI 包下载 + const allowedPypiHosts = ["pypi.org", "files.pythonhosted.org"]; + try { + const parsedUrl = new URL(href); + if (allowedPypiHosts.includes(parsedUrl.host)) { + // PyPI 包文件下载 + if ( + pathname.includes("/packages/") && + (pathname.endsWith(".tar.gz") || + pathname.endsWith(".whl") || + pathname.endsWith(".egg")) + ) { + return true; + } + } + } catch (e) { + console.error("无效的 URL:", href, e); + } + + // 第八检查:SourceForge 下载 + const allowedSourceForgeHosts = ["sourceforge.net"]; + try { + const parsedUrl = new URL(href); + if (allowedSourceForgeHosts.includes(parsedUrl.host)) { + // SourceForge 下载 URL 包含 /download + if ( + pathname.includes("/download") || + url.searchParams.get("use_mirror") + ) { + return true; + } + } + } catch (e) { + console.error("无效的 URL:", href, e); + } + + // 第九检查:Conda 包下载 + const allowedCondaHosts = ["repo.anaconda.com", "conda.anaconda.org"]; + try { + const parsedUrl = new URL(href); + if (allowedCondaHosts.includes(parsedUrl.host)) { + // Conda 包文件 + if (pathname.endsWith(".conda") || pathname.endsWith(".tar.bz2")) { + return true; + } + } + } catch (e) { + console.error("无效的 URL:", href, e); + } + + // 第十检查:其他包管理平台 + const packageManagerHosts = [ + "rubygems.org", + "cran.r-project.org", + "crates.io", + "repo.packagist.org", + "api.nuget.org", + "proxy.golang.org", + ]; + try { + const parsedUrl = new URL(href); + if (packageManagerHosts.includes(parsedUrl.host)) { + // 各种包管理器的下载文件 + const packageExtensions = [ + ".gem", + ".tar.gz", + ".crate", + ".zip", + ".nupkg", + ".tgz", + ".tar.bz2", + ]; + if (packageExtensions.some((ext) => pathname.endsWith(ext))) { + return true; + } + } + } catch (e) { + console.error("无效的 URL:", href, e); + } + + // 第十一检查:明确的下载文本指示器(更具体) const downloadTextIndicators = ["download", "download file", "get file"]; const linkText = link.textContent.toLowerCase().trim(); if ( diff --git a/manifest.json b/manifest.json index ec2991a..c732ab6 100644 --- a/manifest.json +++ b/manifest.json @@ -1,10 +1,10 @@ { "action": { "default_icon": { + "128": "icons/icon128.png", "16": "icons/icon16.png", "32": "icons/icon32.png", - "48": "icons/icon48.png", - "128": "icons/icon128.png" + "48": "icons/icon48.png" }, "default_popup": "popup.html", "default_title": "Xget for Chromium" @@ -14,45 +14,79 @@ }, "content_scripts": [ { - "js": [ - "content.js" - ], + "js": ["content.js"], "matches": [ "https://github.com/*", "https://gitlab.com/*", - "https://huggingface.co/*" + "https://gitea.com/*", + "https://codeberg.org/*", + "https://sourceforge.net/*", + "https://android.googlesource.com/*", + "https://huggingface.co/*", + "https://registry.npmjs.org/*", + "https://pypi.org/*", + "https://files.pythonhosted.org/*", + "https://repo.anaconda.com/*", + "https://conda.anaconda.org/*", + "https://repo1.maven.org/*", + "https://downloads.apache.org/*", + "https://plugins.gradle.org/*", + "https://rubygems.org/*", + "https://cran.r-project.org/*", + "https://www.cpan.org/*", + "https://tug.ctan.org/*", + "https://proxy.golang.org/*", + "https://api.nuget.org/*", + "https://crates.io/*", + "https://repo.packagist.org/*", + "https://arxiv.org/*", + "https://f-droid.org/*" ], "run_at": "document_end" } ], - "description": "为 GitHub、GitLab 和 Hugging Face 提供更快的文件下载体验", + "description": "为 Chromium 浏览器提供的 Xget 下载加速扩展,支持 GitHub、GitLab、npm、PyPI 等众多平台的文件下载加速", "host_permissions": [ "https://github.com/*", "https://gitlab.com/*", - "https://huggingface.co/*" + "https://gitea.com/*", + "https://codeberg.org/*", + "https://sourceforge.net/*", + "https://android.googlesource.com/*", + "https://huggingface.co/*", + "https://registry.npmjs.org/*", + "https://pypi.org/*", + "https://files.pythonhosted.org/*", + "https://repo.anaconda.com/*", + "https://conda.anaconda.org/*", + "https://repo1.maven.org/*", + "https://downloads.apache.org/*", + "https://plugins.gradle.org/*", + "https://rubygems.org/*", + "https://cran.r-project.org/*", + "https://www.cpan.org/*", + "https://tug.ctan.org/*", + "https://proxy.golang.org/*", + "https://api.nuget.org/*", + "https://crates.io/*", + "https://repo.packagist.org/*", + "https://arxiv.org/*", + "https://f-droid.org/*" ], "icons": { + "128": "icons/icon128.png", "16": "icons/icon16.png", "32": "icons/icon32.png", - "48": "icons/icon48.png", - "128": "icons/icon128.png" + "48": "icons/icon48.png" }, "manifest_version": 3, "name": "Xget for Chromium", - "permissions": [ - "downloads", - "storage", - "activeTab" - ], - "version": "1.3.0", + "permissions": ["downloads", "storage", "activeTab"], + "version": "2.0.0", "web_accessible_resources": [ { - "matches": [ - "" - ], - "resources": [ - "icons/*" - ] + "matches": [""], + "resources": ["icons/*"] } ] -} \ No newline at end of file +} diff --git a/package.json b/package.json index 4543b8f..475d623 100644 --- a/package.json +++ b/package.json @@ -1,39 +1,63 @@ { "author": "Xi Xu", - "browser": "chrome", "bugs": { "url": "https://github.com/xixu-me/Xget-for-Chromium/issues" }, - "dependencies": {}, - "description": "为 GitHub、GitLab 和 Hugging Face 提供更快的文件下载体验", - "devDependencies": {}, - "homepage": "https://github.com/xixu-me/Xget-for-Chromium#readme", + "description": "为 Chromium 浏览器提供的 Xget 下载加速扩展,支持 GitHub、GitLab、npm、PyPI 等众多平台的文件下载加速", + "engines": { + "chrome": ">=88" + }, + "homepage": "https://github.com/xixu-me/Xget-for-Chromium", "keywords": [ "chrome-extension", "download-accelerator", - "xget", - "proxy", "github", "gitlab", - "huggingface" + "npm", + "pypi", + "xget", + "download-proxy" ], "license": "GPL-3.0", "main": "background.js", - "manifest_version": 3, + "manifest": { + "platforms": [ + "github.com", + "gitlab.com", + "gitea.com", + "codeberg.org", + "sourceforge.net", + "android.googlesource.com", + "huggingface.co", + "registry.npmjs.org", + "pypi.org", + "files.pythonhosted.org", + "repo.anaconda.com", + "conda.anaconda.org", + "repo1.maven.org", + "downloads.apache.org", + "plugins.gradle.org", + "rubygems.org", + "cran.r-project.org", + "www.cpan.org", + "tug.ctan.org", + "proxy.golang.org", + "api.nuget.org", + "crates.io", + "repo.packagist.org", + "arxiv.org", + "f-droid.org" + ], + "version": 3 + }, "name": "xget-for-chromium", "repository": { "type": "git", "url": "https://github.com/xixu-me/Xget-for-Chromium.git" }, "scripts": { - "build": "不需要构建过程 - 扩展直接从源码运行", - "clean": "rm -f *.zip", - "dev": "打开 chrome://extensions/ 并加载解压的扩展", - "lint": "未配置代码检查 - 建议为 JavaScript 文件添加 ESLint", - "package": "zip -r extension.zip . -x '*.git*' 'node_modules/*' '.github/*' '*.md' 'LICENSE' '*.zip'", - "release": "npm run validate && npm run package", - "test": "在 Chrome 开发者模式下加载扩展进行测试", - "validate": "node -e \"JSON.parse(require('fs').readFileSync('manifest.json', 'utf8')); console.log('✅ manifest.json 格式有效')\"" + "build": "echo 'No build process needed for Chrome extension'", + "test": "echo 'No tests specified'" }, - "version": "1.3.0" -} \ No newline at end of file + "version": "2.0.0" +} diff --git a/platforms.js b/platforms.js new file mode 100644 index 0000000..c7beb47 --- /dev/null +++ b/platforms.js @@ -0,0 +1,215 @@ +/** + * 平台配置对象,包含各个平台的基础URL和名称 + * @type {Object.} + */ +export const PLATFORMS = { + // 代码托管平台 + gh: { + base: "https://github.com", + name: "GitHub", + pattern: /^https:\/\/github\.com\//, + }, + gl: { + base: "https://gitlab.com", + name: "GitLab", + pattern: /^https:\/\/gitlab\.com\//, + }, + gitea: { + base: "https://gitea.com", + name: "Gitea", + pattern: /^https:\/\/gitea\.com\//, + }, + codeberg: { + base: "https://codeberg.org", + name: "Codeberg", + pattern: /^https:\/\/codeberg\.org\//, + }, + sf: { + base: "https://sourceforge.net", + name: "SourceForge", + pattern: /^https:\/\/sourceforge\.net\//, + }, + aosp: { + base: "https://android.googlesource.com", + name: "AOSP", + pattern: /^https:\/\/android\.googlesource\.com\//, + }, + + // AI/ML 平台 + hf: { + base: "https://huggingface.co", + name: "Hugging Face", + pattern: /^https:\/\/huggingface\.co\//, + }, + + // 包管理平台 + npm: { + base: "https://registry.npmjs.org", + name: "npm", + pattern: /^https:\/\/registry\.npmjs\.org\//, + }, + pypi: { + base: "https://pypi.org", + name: "PyPI", + pattern: /^https:\/\/pypi\.org\//, + }, + "pypi-files": { + base: "https://files.pythonhosted.org", + name: "PyPI Files", + pattern: /^https:\/\/files\.pythonhosted\.org\//, + }, + conda: { + base: "https://repo.anaconda.com", + name: "Conda", + pattern: /^https:\/\/repo\.anaconda\.com\//, + }, + "conda-community": { + base: "https://conda.anaconda.org", + name: "Conda Community", + pattern: /^https:\/\/conda\.anaconda\.org\//, + }, + maven: { + base: "https://repo1.maven.org", + name: "Maven", + pattern: /^https:\/\/repo1\.maven\.org\//, + }, + apache: { + base: "https://downloads.apache.org", + name: "Apache", + pattern: /^https:\/\/downloads\.apache\.org\//, + }, + gradle: { + base: "https://plugins.gradle.org", + name: "Gradle", + pattern: /^https:\/\/plugins\.gradle\.org\//, + }, + rubygems: { + base: "https://rubygems.org", + name: "RubyGems", + pattern: /^https:\/\/rubygems\.org\//, + }, + cran: { + base: "https://cran.r-project.org", + name: "CRAN", + pattern: /^https:\/\/cran\.r-project\.org\//, + }, + cpan: { + base: "https://www.cpan.org", + name: "CPAN", + pattern: /^https:\/\/www\.cpan\.org\//, + }, + ctan: { + base: "https://tug.ctan.org", + name: "CTAN", + pattern: /^https:\/\/tug\.ctan\.org\//, + }, + golang: { + base: "https://proxy.golang.org", + name: "Go Modules", + pattern: /^https:\/\/proxy\.golang\.org\//, + }, + nuget: { + base: "https://api.nuget.org", + name: "NuGet", + pattern: /^https:\/\/api\.nuget\.org\//, + }, + crates: { + base: "https://crates.io", + name: "Crates.io", + pattern: /^https:\/\/crates\.io\//, + }, + packagist: { + base: "https://repo.packagist.org", + name: "Packagist", + pattern: /^https:\/\/repo\.packagist\.org\//, + }, + + // 其他平台 + arxiv: { + base: "https://arxiv.org", + name: "arXiv", + pattern: /^https:\/\/arxiv\.org\//, + }, + fdroid: { + base: "https://f-droid.org", + name: "F-Droid", + pattern: /^https:\/\/f-droid\.org\//, + }, +}; + +/** + * 统一的路径转换函数 + * @param {string} path - 原始路径 + * @param {string} platformKey - 平台键值 + * @returns {string} - 转换后的路径 + */ +export function transformPath(path, platformKey) { + if (!PLATFORMS[platformKey]) { + return path; + } + + const prefix = `/${platformKey.replace(/-/g, "/")}/`; + let transformedPath = path.replace( + new RegExp(`^${prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`), + "/" + ); + + // 特殊处理 crates.io API 路径 + if (platformKey === "crates") { + // 转换路径以包含 API 前缀 + if (transformedPath.startsWith("/")) { + // 处理不同的 API 端点: + // /serde/1.0.0/download -> /api/v1/crates/serde/1.0.0/download + // /serde -> /api/v1/crates/serde + // /?q=query -> /api/v1/crates?q=query + if (transformedPath === "/" || transformedPath.startsWith("/?")) { + // 搜索端点 + transformedPath = transformedPath.replace("/", "/api/v1/crates"); + } else { + // 特定 crate 端点 + transformedPath = `/api/v1/crates${transformedPath}`; + } + } + } + + return transformedPath; +} + +/** + * 根据URL检测平台 + * @param {string} url - 要检测的URL + * @returns {string|null} - 平台键值或null + */ +export function detectPlatform(url) { + for (const [key, platform] of Object.entries(PLATFORMS)) { + if (platform.pattern.test(url)) { + return key; + } + } + return null; +} + +/** + * 转换URL为Xget格式 + * @param {string} url - 原始URL + * @param {string} xgetDomain - Xget域名 + * @param {Object} enabledPlatforms - 启用的平台配置 + * @returns {string|null} - 转换后的URL或null + */ +export function transformUrl(url, xgetDomain, enabledPlatforms) { + try { + const platform = detectPlatform(url); + if (!platform || !enabledPlatforms[platform]) { + return null; + } + + const urlObj = new URL(url); + const path = urlObj.pathname + urlObj.search + urlObj.hash; + const transformedPath = transformPath(path, platform); + + return `https://${xgetDomain}/${platform}${transformedPath}`; + } catch (error) { + console.error("转换 URL 时出错:", error); + return null; + } +} diff --git a/popup.html b/popup.html index c62198d..24f9f33 100644 --- a/popup.html +++ b/popup.html @@ -7,7 +7,7 @@