diff --git a/astral/api/MaimaiNet.py b/astral/api/MaimaiNet.py index 5783e76..5145180 100644 --- a/astral/api/MaimaiNet.py +++ b/astral/api/MaimaiNet.py @@ -11,17 +11,17 @@ MaimaiNet.py —— 舞萌DX 网络核心层(纯 HTTP 复刻) 1) 服务器基址与 API 路径 source/Assembly-CSharp/Manager/OperationManager.cs:225,338 - ServerUri = Auth.GameServerUri + MovieServerUri("/") + ServerUri = Auth.GameServerUri + MovieServerUri("/") source/Assembly-CSharp/Net.Packet/Packet.cs:116 Client = NetHttpClient.Create(BaseUrl + Obfuscator(query.Api)) source/Assembly-CSharp/Net.Packet/Packet.cs:485 - Obfuscator(s) = md5_hex_lower(s + "") + Obfuscator(s) = md5_hex_lower(s + "") source/Assembly-CSharp/Net.VO/NetQuery.cs:14 Api = api + "MaimaiChn" - => 最终 URL = BASE_URL + md5(逻辑接口名 + "MaimaiChn" + "") + => 最终 URL = BASE_URL + md5(逻辑接口名 + "MaimaiChn" + "") - 已用真实日志验证:md5("GetGameSettingApiMaimaiChn" + "") + 已用真实日志验证:md5("GetGameSettingApiMaimaiChn" + "") = 83a5d5a20b062c5c2ad817460b8f7f76 (source/2026-10-05.log:1 的请求就是打到这个尾段) @@ -121,7 +121,7 @@ from config import CLIENT_ID, MAI_ENCODING, TIMEOUT_LONG_MS, TIMEOUT_MS #: 游戏服务器(Maimai2Servlet) #: 依据 source/Assembly-CSharp/Manager/OperationManager.cs:225,338 -#: _operationData.ServerUri = Auth.GameServerUri + MovieServerUri("/") +#: _operationData.ServerUri = Auth.GameServerUri + MovieServerUri("/") BASE_URL = "" #: AES 参数,依据 source/Assembly-CSharp/Net/CipherAES.cs:11-12 @@ -162,7 +162,7 @@ def obfuscate_api_name(api_name: str) -> str: 对逻辑接口名做 MD5 混淆,得到 URL 尾段(对应 Packet.Obfuscator)。 逻辑名 "GetUserPreviewApi" -> "GetUserPreviewApiMaimaiChn" - -> md5(上面 + "") + -> md5(上面 + "") 参数: api_name: 逻辑接口名,**不要**自己带 MaimaiChn 后缀 diff --git a/maipulse/app.py b/maipulse/app.py index 99d96ab..e287911 100644 --- a/maipulse/app.py +++ b/maipulse/app.py @@ -912,36 +912,72 @@ def api_update_gh(): repo=repo) +UPDATE_SITE = "https://git.fragrance.moe" +UPDATE_REPO = "shouko/maipulse" + + @app.get("/api/update-check") -@guarded def api_update_check(): - """拉远端 version.json 与本地比较。远端格式: - {"version": "1.0.1", "url": "下载地址", "notes": "更新说明", "published": "..."} + """检查更新。默认走自建 Gitea 的 Release(公开仓库,不用 token)。 + + asset 用 browser_download_url —— 那是 /releases/download// 的稳定地址, + 比 attachments/uuid 可靠。给了 source 参数则按老的远端 version.json 走。 """ import urllib.request - src = (request.args.get("source") or "").strip() - if not src: - return fail("还没填更新源地址") - if not re.match(r"^https?://", src): - return fail("更新源必须是 http(s) 地址") - try: - req = urllib.request.Request(src, headers={"User-Agent": "maipulse/" + APP_VERSION}) - raw = urllib.request.urlopen(req, timeout=20).read().decode("utf-8") - remote = json.loads(raw) - except Exception as exc: # noqa: BLE001 - return fail("拉取更新源失败:%s" % exc) - ver = str(remote.get("version") or "") - if not ver: - return fail("更新源里没有 version 字段") + src = (request.args.get("source") or "").strip() def _v(text): - return tuple(int(x) for x in re.findall(r"\d+", text)[:3]) + nums = [int(x) for x in re.findall(r"\d+", str(text))[:3]] + while len(nums) < 3: + nums.append(0) + return tuple(nums) - newer = _v(ver) > _v(APP_VERSION) - return ok(current=APP_VERSION, remote=ver, newer=newer, - url=remote.get("url"), notes=remote.get("notes"), - published=remote.get("published")) + if src: + if not re.match(r"^https?://", src): + return fail("更新源必须是 http(s) 地址") + try: + req = urllib.request.Request(src, headers={"User-Agent": "maipulse/" + APP_VERSION}) + remote = json.loads(urllib.request.urlopen(req, timeout=20).read().decode("utf-8")) + except Exception as exc: # noqa: BLE001 + return fail("拉取更新源失败:%s" % exc) + ver = str(remote.get("version") or "") + if not ver: + return fail("更新源里没有 version 字段") + return ok(current=APP_VERSION, remote=ver, newer=_v(ver) > _v(APP_VERSION), + url=remote.get("url"), notes=remote.get("notes"), + published=remote.get("published"), repo="(自填)") + + api = "%s/api/v1/repos/%s/releases/latest" % (UPDATE_SITE, UPDATE_REPO) + try: + req = urllib.request.Request(api, headers={"User-Agent": "maipulse/" + APP_VERSION}) + rel = json.loads(urllib.request.urlopen(req, timeout=20).read().decode("utf-8")) + except urllib.error.HTTPError as exc: + return fail("Release 接口 HTTP %s(仓库或 Release 不存在)" % exc.code) + except Exception as exc: # noqa: BLE001 + return fail("连不上更新仓库:%s" % exc) + + tag = str(rel.get("tag_name") or "").strip() + if not tag: + return fail("Release 没有 tag") + ver = re.sub(r"^[vV]", "", tag) + + assets = rel.get("assets") or [] + apk = next((a for a in assets if str(a.get("name", "")).lower().endswith(".apk")), None) + if apk: + url = apk.get("browser_download_url") or "" + size = apk.get("size") or 0 + name = apk.get("name") or "maipulse.apk" + else: + url = "%s/%s/releases/download/%s/maipulse.apk" % (UPDATE_SITE, UPDATE_REPO, tag) + size = 0 + name = "maipulse.apk" + return ok(current=APP_VERSION, remote=ver, newer=_v(ver) > _v(APP_VERSION), + url=url, name=name, size=size, tag=tag, + notes=(rel.get("body") or "")[:500], + published=rel.get("published_at"), + page="%s/%s/releases/tag/%s" % (UPDATE_SITE, UPDATE_REPO, tag), + repo="%s/%s" % (UPDATE_REPO.split("/")[0], UPDATE_REPO.split("/")[-1])) # ---------------------------------------------------------------- 配置 diff --git a/maipulse/html/index.html b/maipulse/html/index.html index 5307f99..10a1a39 100644 --- a/maipulse/html/index.html +++ b/maipulse/html/index.html @@ -657,6 +657,12 @@ +
最新版本—
+
+ + +
+ @@ -1284,6 +1290,31 @@ function importConf(file){ /* ---------- 更新 ---------- *//* ---------- 更新 ---------- */ const UP_SRC_KEY = 'maipulse.updatesrc'; + +var _upd = null; +async function checkUpdate(){ + var box = document.getElementById('verRemote'); + box.textContent = '检查中…'; + try{ + var r = await (await fetch('/api/update-check')).json(); + if(!r.ok){ box.textContent = '检查失败'; log('检查更新:' + r.error, 1); return; } + _upd = r; + box.textContent = r.remote + (r.newer ? '(有新版)' : '(已是最新)'); + var note = document.getElementById('verNote'); + if(r.notes){ note.style.display = ''; note.textContent = r.notes; } + document.getElementById('verDl').style.display = r.newer ? '' : 'none'; + }catch(e){ box.textContent = '检查失败'; log('检查更新:' + e, 1); } +} +function downloadUpdate(){ + if(!_upd || !_upd.url){ log('没有可下载的地址', 1); return; } + // 安卓壳里有原生下载器,没有就新开标签 + if(window.Maipulse && window.Maipulse.installApk){ + window.Maipulse.installApk(_upd.url); + }else{ + window.open(_upd.url, '_blank'); + } +} + async function loadVersion(){ const r = await get('/api/version'); if(!r.ok) return;