diff --git a/extension.js b/extension.js index dab0fe8..5fe1e15 100644 --- a/extension.js +++ b/extension.js @@ -49,12 +49,10 @@ shuYing.initFunction = () => { }; shuYing.getOnlineVersionText = () => `最新版本:${lib.config.shuYing_online_version || "检测中..."}`; -shuYing.updateOnlineVersionMenu = (version) => { - if (!version) return; +shuYing.updateOnlineVersionMenu = (version, status = "检测中...") => { + if (version) game.saveConfig("shuYing_online_version", version); - game.saveConfig("shuYing_online_version", version); - - const text = `最新版本:${version}`; + const text = `最新版本:${version || status}`; if (lib.extensionMenu.extension_术樱包?.online_version) { lib.extensionMenu.extension_术樱包.online_version.name = text; } @@ -113,8 +111,11 @@ shuYing.mergeCharacterPack = function (target, source, sourceName = "附加模 }; shuYing.getVersion = (callback) => { + const requestId = (shuYing.versionRequestId || 0) + 1; + shuYing.versionRequestId = requestId; return updater.getOnlineVersion() .then(version => { + if (requestId != shuYing.versionRequestId) return null; shuYing.updateOnlineVersionMenu(version?.online_version); if (typeof callback == "function") { callback(version); @@ -122,7 +123,9 @@ shuYing.getVersion = (callback) => { return version; }) .catch(error => { + if (requestId != shuYing.versionRequestId) return null; console.error("术樱包获取在线版本失败:", error); + shuYing.updateOnlineVersionMenu(null, "获取失败"); return null; }); } diff --git a/menu.js b/menu.js index 6dd7fbb..2669a7a 100644 --- a/menu.js +++ b/menu.js @@ -210,7 +210,7 @@ export default function initShuYingMenu({ lib, game, ui, shuYing, updater }) { menu.update_channel = { name: channelText(updater.getUpdateChannel()), clear: true, - intro: "切换后立即从 Gitea 对应标签下载并校验文件,完成后需重启游戏。测试版可能回退到较早的内容。", + intro: "切换后立即从当前下载源的对应标签下载并校验文件,完成后需重启游戏。测试版可能回退到较早的内容。", async onclick() { if (!shuYing.m_bIsDownload) { alert("有其他文件正在下载,请稍后再试吧。"); @@ -239,6 +239,28 @@ export default function initShuYingMenu({ lib, game, ui, shuYing, updater }) { }, }; + const sourceText = source => `下载源:${source == "github" ? "GitHub" : "Gitea"}(点击切换)`; + menu.update_source = { + name: sourceText(updater.getUpdateSource()), + clear: true, + intro: "选择从 GitHub 或 Gitea 获取标签与文件;切换后刷新在线版本,实际下载由版本检测触发。", + onclick() { + if (!shuYing.m_bIsDownload) { + alert("有其他文件正在下载,请稍后再试吧。"); + return; + } + + const next = updater.getUpdateSource() == "github" ? "gitea" : "github"; + game.saveConfig("shuYing_update_source", next); + const text = sourceText(next); + menu.update_source.name = text; + this.innerHTML = text; + game.saveConfig("shuYing_online_version", null); + shuYing.updateOnlineVersionMenu(null, "检测中..."); + shuYing.getVersion(); + }, + }; + menu.updateLog = { name: '
', clear: true, diff --git a/update.js b/update.js index e31a0b0..818d703 100644 --- a/update.js +++ b/update.js @@ -1,6 +1,8 @@ import { lib, game } from "../../noname.js"; -const repositoryApi = "https://gitea.diuse.work/api/v1/repos/Diuse/noname-shuying"; +const giteaRepositoryApi = "https://gitea.diuse.work/api/v1/repos/Diuse/noname-shuying"; +const githubRepositoryApi = "https://api.github.com/repos/Shu-Ying/noname-shuying"; +const githubRawBase = "https://raw.githubusercontent.com/Shu-Ying/noname-shuying"; const coreTempDir = "extension/术樱包/.update_tmp"; function parseVersion(version) { @@ -30,15 +32,25 @@ function getUpdateChannel() { ? "preview" : "stable"); } -function getRemoteUrl(tag, path) { - const filePath = path.split("/").map(encodeURIComponent).join("/"); - return `${repositoryApi}/raw/${filePath}?ref=${encodeURIComponent(tag)}`; +function getUpdateSource() { + return lib.config.shuYing_update_source == "github" ? "github" : "gitea"; } -async function getLatestTag(channel = getUpdateChannel()) { +function getRemoteUrl(tag, path, source = getUpdateSource()) { + const filePath = path.split("/").map(encodeURIComponent).join("/"); + if (source == "github") { + return `${githubRawBase}/${encodeURIComponent(tag)}/${filePath}`; + } + return `${giteaRepositoryApi}/raw/${filePath}?ref=${encodeURIComponent(tag)}`; +} + +async function getLatestTag(channel = getUpdateChannel(), source = getUpdateSource()) { let latest = null; for (let page = 1; ; page++) { - const response = await fetch(`${repositoryApi}/tags?page=${page}&limit=50`); + const tagsUrl = source == "github" + ? `${githubRepositoryApi}/tags?page=${page}&per_page=50` + : `${giteaRepositoryApi}/tags?page=${page}&limit=50`; + const response = await fetch(tagsUrl); if (!response.ok) throw new Error(`标签请求失败:${response.status}`); const tags = await response.json(); if (!Array.isArray(tags)) throw new Error("标签列表格式错误"); @@ -55,7 +67,7 @@ async function getLatestTag(channel = getUpdateChannel()) { } if (tags.length < 50) break; } - if (!latest) throw new Error(`Gitea 镜像尚无${channel == "preview" ? "测试版" : "正式版"}标签`); + if (!latest) throw new Error(`更新源尚无${channel == "preview" ? "测试版" : "正式版"}标签`); return latest; } @@ -192,8 +204,8 @@ function splitPath(path) { }; } -async function fetchFile(path, tag) { - const response = await fetch(getRemoteUrl(tag, path)); +async function fetchFile(path, tag, source) { + const response = await fetch(getRemoteUrl(tag, path, source)); if (!response.ok) throw new Error(`${path} 下载失败:${response.status}`); return await response.arrayBuffer(); @@ -209,7 +221,7 @@ async function writeTargetFile(data, target) { } async function downloadFile(path, target, manifest) { - const data = await fetchFile(path, manifest.tag_name); + const data = await fetchFile(path, manifest.tag_name, manifest.source); if (manifest?.files?.[path]) { await verifyFile(path, data, manifest); } @@ -217,8 +229,9 @@ async function downloadFile(path, target, manifest) { return data; } -async function getManifestFromTag(tag, requireChannels = true) { - const response = await fetch(getRemoteUrl(tag.name, "dist/manifest.json")); +async function getManifestFromTag(tag, requireChannels = true, + source = getUpdateSource()) { + const response = await fetch(getRemoteUrl(tag.name, "dist/manifest.json", source)); if (!response.ok) throw new Error(`校验清单请求失败:${response.status}`); const manifest = await response.json(); @@ -246,11 +259,12 @@ async function getManifestFromTag(tag, requireChannels = true) { getManifestRemoveDirectories(manifest); manifest.tag_name = tag.name; + manifest.source = source; return manifest; } -async function getManifest(channel = getUpdateChannel()) { - return getManifestFromTag(await getLatestTag(channel)); +async function getManifest(channel = getUpdateChannel(), source = getUpdateSource()) { + return getManifestFromTag(await getLatestTag(channel, source), true, source); } function normalizeManifestPath(path) { @@ -727,8 +741,9 @@ async function downloadList(shuYing, files, manifest) { } } -async function getOnlineVersion(channel = getUpdateChannel()) { - const manifest = await getManifest(channel); +async function getOnlineVersion(channel = getUpdateChannel(), + source = getUpdateSource()) { + const manifest = await getManifest(channel, source); return { online_version: manifest.version, manifest }; } @@ -736,19 +751,19 @@ async function checkVersion(shuYing, options = {}) { try { shuYing._updateLogs = []; await addLog(shuYing, "版本检查开始"); - const onlineVersion = await getOnlineVersion(options.channel); + const onlineVersion = await getOnlineVersion(options.channel, options.source); const localVersion = lib.config.shuYing_local_version || "0.0.0.0"; const manifest = onlineVersion.manifest; if (!options.switchChannel && parseVersion(localVersion) && compareVersions(onlineVersion.online_version, localVersion) < 0) { - throw new Error("Gitea 镜像尚未同步到本地版本,请稍后重试"); + throw new Error("目标更新源的版本落后于本地版本,请稍后重试"); } if (options.switchChannel && parseVersion(localVersion) && localVersion != manifest.version) { const previous = await getManifestFromTag({ name: `v${localVersion}`, version: localVersion, - }, false); + }, false, manifest.source); const targetFiles = new Set(getManifestFileKeys(manifest)); const removed = getManifestFileKeys(previous) .filter(file => !targetFiles.has(file)); @@ -757,7 +772,7 @@ async function checkVersion(shuYing, options = {}) { } const changed = await getChangedFiles(manifest); const obsolete = await getObsoleteFiles(manifest); - await addLog(shuYing, `Gitea 标签:${manifest.tag_name};待更新文件:${changed.length};待删除文件:${obsolete.length}`); + await addLog(shuYing, `更新源:${manifest.source};标签:${manifest.tag_name};待更新文件:${changed.length};待删除文件:${obsolete.length}`); if (localVersion == onlineVersion.online_version && !changed.length && !obsolete.length) { @@ -885,6 +900,7 @@ async function repairCoreFiles(shuYing) { export default { getOnlineVersion, getUpdateChannel, + getUpdateSource, checkVersion, repairMissingFiles, repairCoreFiles,