Files
xget-now/background.js
T
xixu-me ed3a4c0ee3 Prevent multiple suggest calls in handleDownload
Introduces a guard to ensure the suggest callback is only called once in handleDownload, avoiding potential issues from multiple invocations in error or early return scenarios.
2025-08-10 12:12:23 +08:00

330 lines
7.8 KiB
JavaScript

// 平台配置
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\//,
},
};
// 转换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,
},
};
// 初始化扩展
chrome.runtime.onInstalled.addListener(async () => {
console.log("Xget Now 已安装");
// 如果尚未设置,则设置默认设置
const settings = await chrome.storage.sync.get(DEFAULT_SETTINGS);
await chrome.storage.sync.set(settings);
});
// 监听下载事件
chrome.downloads.onDeterminingFilename.addListener((downloadItem, suggest) => {
handleDownload(downloadItem, suggest);
});
async function handleDownload(downloadItem, suggest) {
let suggestionCalled = false;
const callSuggest = () => {
if (!suggestionCalled) {
suggestionCalled = true;
suggest();
}
};
try {
const settings = await chrome.storage.sync.get(DEFAULT_SETTINGS);
// 检查扩展是否已启用并配置了域名
if (!settings.enabled || !settings.xgetDomain) {
callSuggest();
return;
}
const url = downloadItem.url;
const redirectedUrl = transformUrl(
url,
settings.xgetDomain,
settings.enabledPlatforms
);
if (redirectedUrl && redirectedUrl !== url) {
console.log("重定向下载:", url, "->", redirectedUrl);
// 取消原始下载
chrome.downloads.cancel(downloadItem.id);
// 使用重定向的 URL 开始新下载
chrome.downloads.download({
url: redirectedUrl,
filename: downloadItem.filename || undefined,
conflictAction: "uniquify",
});
// 通过内容脚本显示通知
try {
await chrome.tabs.sendMessage(downloadItem.tabId, {
action: "showNotification",
message: "下载已通过 Xget 重定向",
});
} catch (error) {
console.log("无法向标签页发送通知");
}
} else {
callSuggest();
}
} catch (error) {
console.error("处理下载时出错:", error);
callSuggest();
}
}
function transformUrlLegacy(url, settings) {
try {
// 找到匹配的平台
for (const [platformKey, platform] of Object.entries(PLATFORMS)) {
if (!settings.enabledPlatforms[platformKey]) continue;
if (platform.pattern.test(url)) {
const urlObj = new URL(url);
const path = urlObj.pathname + urlObj.search + urlObj.hash;
// 使用 Xget 域名转换 URL(添加 https:// 协议)
const xgetUrl = `https://${settings.xgetDomain}/${platformKey}${path}`;
return xgetUrl;
}
}
return null; // 不需要转换
} catch (error) {
console.error("转换 URL 时出错:", error);
return null;
}
}
// 监听来自弹出窗口/选项的消息
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === "getSettings") {
chrome.storage.sync.get(DEFAULT_SETTINGS).then(sendResponse);
return true;
} else if (request.action === "saveSettings") {
chrome.storage.sync.set(request.settings).then(async () => {
// 通知相关标签页刷新
try {
const tabs = await chrome.tabs.query({
url: Object.values(PLATFORMS).map((platform) => platform.base + "/*"),
});
for (const tab of tabs) {
try {
await chrome.tabs.sendMessage(tab.id, {
action: "showNotification",
message: "设置已更新!点击刷新页面",
showRefreshButton: true,
});
} catch (error) {
// 标签页可能没有加载内容脚本,忽略
}
}
} catch (error) {
console.log("无法通知标签页设置更新");
}
sendResponse({ success: true });
});
return true;
}
});