Files
MaaAssistantArknights/src/Python/asst/updater.py
2025-05-15 23:19:13 +08:00

232 lines
9.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import json
import multiprocessing
import platform
import re
import os
import tarfile
import zipfile
from multiprocessing import queues, Process
from urllib import request
from urllib.error import HTTPError, URLError
from .asst import Asst
from .utils import Version
from . import downloader
class Updater:
# API的地址
Mirrors = ["https://api.maa.plus"]
Summary_json = "/MaaAssistantArknights/api/version/summary.json"
@staticmethod
def custom_print(s):
"""
可以被monkey patch的print在其他GUI上使用可以被替换为任何需要的输出
"""
print(s)
@staticmethod
def _get_cur_version(path, q):
"""
从MaaCore.dll获取当前版本号
这里是复用原来的方法
"""
Asst.load(path=path)
q.put(Asst().get_version())
def __init__(self, path, version):
self.path = path
self.version = version
self.latest_json = None
self.latest_version = None
self.assets_object = None
# 使用子线程获取当前版本后关闭避免占用dll
q = queues.Queue(1, ctx=multiprocessing)
p = Process(target=self._get_cur_version, args=(path, q,))
p.start()
p.join()
# MAA当前版本 self.cur_version
self.cur_version = q.get()
@staticmethod
def map_version_type(version):
type_map = {
Version.Nightly: 'alpha',
Version.Beta: 'beta',
Version.Stable: 'stable'
}
return type_map.get(version, 'stable')
def get_latest_version(self):
"""
从API获取最新版本
"""
api_url = self.Mirrors
version_summary = self.Summary_json
retry = 3
for retry_times in range(retry):
# 在重试次数限制内依次请求每一个镜像
i = retry_times % len(api_url)
request_url = api_url[i] + version_summary
try:
response_json = request.urlopen(request_url)
response_data = json.loads(response_json.read().decode("utf-8"))
"""
解析JSON
e.g.
{
"alpha": {
"version": "v4.24.0-beta.1.d006.g27dee653d",
"detail": "https://api.maa.plus/MaaAssistantArknights/api/version/alpha.json"
},
"beta": {
"version": "v4.24.0-beta.1",
"detail": "https://api.maa.plus/MaaAssistantArknights/api/version/beta.json"
},
"stable": {
"version": "v4.23.3",
"detail": "https://api.maa.plus/MaaAssistantArknights/api/version/stable.json"
}
}
"""
version_type = self.map_version_type(self.version)
latest_version = response_data[version_type]['version']
version_detail = response_data[version_type]['detail']
return latest_version, version_detail
except Exception as e:
self.custom_print(e)
continue
return False, False
@staticmethod
def get_download_url(detail):
"""
1.获取系统及架构信息
2.找到对应的版本
3.返回镜像url列表&文件名
"""
"""
获取系统信息,包括:
架构ARM、x86
系统Linux、Windows
默认Windows x86_64
"""
system_platform = "win-x64"
system = platform.system()
if system == 'Linux':
machine = platform.machine()
if machine == 'aarch64':
# Linux aarch64
system_platform = "linux-aarch64"
else:
# Linux x86
system_platform = "linux-x86_64"
elif system == 'Windows':
machine = platform.machine()
if machine == 'AMD64' or machine == 'x86_64':
# Windows x86-64
system_platform = "win-x64"
else:
# Windows ARM64
system_platform = "win-arm64"
# 请求的是https://api.maa.plus/MaaAssistantArknights/api/version/stable.json或其他版本类型对应的url
detail_json = request.urlopen(detail)
detail_data = json.loads(detail_json.read().decode("utf-8"))
assets_list = detail_data["details"]["assets"] # 列表,子元素为字典
# 找到对应系统和架构的版本
for assets in assets_list:
"""
结构示例
assets:
{
"name": "MAA-v4.24.0-beta.1.d006.g27dee653d-win-x64.zip",
"size": 145677836,
"browser_download_url": "https://github.com/MaaAssistantArknights/MaaRelease/releases/download/v4.24.0-beta.1.d006.g27dee653d/MAA-v4.24.0-beta.1.d006.g27dee653d-win-x64.zip",
"mirrors": [
"https://s3.maa-org.net:25240/maa-release/MaaAssistantArknights/MaaRelease/releases/download/v4.24.0-beta.1.d006.g27dee653d/MAA-v4.24.0-beta.1.d006.g27dee653d-win-x64.zip",
"https://agent.imgg.dev/MaaAssistantArknights/MaaRelease/releases/download/v4.24.0-beta.1.d006.g27dee653d/MAA-v4.24.0-beta.1.d006.g27dee653d-win-x64.zip",
"https://maa.r2.imgg.dev/MaaAssistantArknights/MaaRelease/releases/download/v4.24.0-beta.1.d006.g27dee653d/MAA-v4.24.0-beta.1.d006.g27dee653d-win-x64.zip"
]
}
"""
assets_name = assets["name"] # 示例值:MAA-v4.24.0-beta.1-win-arm64.zip
# 正则匹配(用于选择当前系统及架构的版本)
# 在线等一个不这么蠢的方法
pattern = r"^MAA-.*-" + re.escape(system_platform) + r"\.(zip|tar\.gz)$"
match = re.match(pattern, assets_name)
if match:
# Mirrors镜像列表
mirrors = assets["mirrors"]
github_url = assets["browser_download_url"]
# 加上GitHub的release链接
mirrors.append(github_url)
return mirrors, assets_name
return False, False
def update(self):
"""
主函数
"""
# 从dll获取MAA的版本
current_version = self.cur_version
# 从API获取最新版本
# latest_version版本号; version_detail对应的json地址
latest_version, version_detail = self.get_latest_version()
if not latest_version: # latest_version为False代表获取失败
self.custom_print("获取版本信息失败")
elif current_version == latest_version: # 通过比较二者是否一致判断是否需要更新(摆烂
self.custom_print("当前为最新版本,无需更新")
else:
self.custom_print(f"检测到最新版本:{latest_version},正在更新")
# 开始更新逻辑
# 解析version_detail的JSON信息
# 通过API获取下载地址列表和对应文件名
url_list, filename = self.get_download_url(version_detail)
if not url_list:
# 如果请求失败则返回False
# 此返回值可能会在非Windows-x86_64的程序更新alpha版时出现
self.custom_print("未找到适用于当前系统的更新包")
# 直接结束
return
# 将路径和文件名拼合成绝对路径
# 默认在maa主程序/MaaCore.dll所在路径下
file = os.path.join(self.path, filename)
# 下载调用Downloader下载器使用url_list镜像url列表和file文件保存路径两个参数
# Proxy参数没加因为可能有问题也可能没问题反正我晚上Clash连不上
# 重试3次
max_retry = 3
for retry_frequency in range(max_retry):
try:
Updater.custom_print("开始下载" + (f",第{retry_frequency}次尝试" if retry_frequency > 1 else ""))
# 调用downloader方法进行下载
downloader.file_download(download_url_list=url_list, download_path=file)
break # RNM怎么会有这么蠢的人忘了写break啊淦
except(HTTPError, URLError) as e:
Updater.custom_print(e)
# 解压下载的文件,
Updater.custom_print('开始安装更新,请不要关闭')
file_extension = os.path.splitext(filename)[1]
unzip = False
# 根据拓展名选择解压算法
# .zip(Windows)/.tar.gz(Linux)
if file_extension == '.zip':
zfile = zipfile.ZipFile(file, 'r')
zfile.extractall(self.path)
zfile.close()
unzip = True
# .tar.gz拓展名的情况按照这个方式得到的拓展名是.gz但是解压的是tar.gz
elif file_extension == '.gz':
tfile = tarfile.open(file, 'r:gz')
tfile.extractall(self.path)
tfile.close()
unzip = True
# 删除压缩包
os.remove(file)
if unzip:
Updater.custom_print('更新完成')
else:
Updater.custom_print('更新未完成')