feat: 优化更新方式

This commit is contained in:
DLmaster361
2025-05-09 14:54:09 +08:00
parent 890886d62d
commit fbfdc6aa12
12 changed files with 158 additions and 344 deletions

View File

@@ -33,7 +33,6 @@ from .core import QueueConfig, MaaConfig, MaaUserConfig, Task, TaskManager, Main
from .models import MaaManager
from .services import Notify, Crypto, System
from .ui import AUTO_MAA
from .utils import DownloadManager
__all__ = [
"QueueConfig",
@@ -47,5 +46,4 @@ __all__ = [
"Crypto",
"System",
"AUTO_MAA",
"DownloadManager",
]

View File

@@ -622,7 +622,7 @@ class MaaUserConfig(QConfig):
class AppConfig(GlobalConfig):
VERSION = "4.3.6.0"
VERSION = "4.3.7.0"
gameid_refreshed = Signal()
PASSWORD_refreshed = Signal()

View File

@@ -26,7 +26,7 @@ v4.3
"""
from loguru import logger
from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtWidgets import QApplication
import sys
import ctypes
import win32gui

View File

@@ -25,20 +25,16 @@ v1.2
作者DLmaster_361
"""
import sys
import json
import zipfile
import requests
import subprocess
import time
import psutil
import win32crypt
import base64
from packaging import version
from functools import partial
from pathlib import Path
from PySide6.QtWidgets import QApplication, QDialog, QVBoxLayout
from PySide6.QtWidgets import QDialog, QVBoxLayout
from qfluentwidgets import (
ProgressBar,
IndeterminateProgressBar,
@@ -46,7 +42,7 @@ from qfluentwidgets import (
setTheme,
Theme,
)
from PySide6.QtGui import QIcon, QCloseEvent
from PySide6.QtGui import QCloseEvent
from PySide6.QtCore import QThread, Signal, QTimer, QEventLoop
from typing import List, Dict, Union
@@ -237,14 +233,9 @@ class DownloadManager(QDialog):
self.version = version
self.config = config
self.download_path = app_path / "DOWNLOAD_TEMP.zip" # 临时下载文件的路径
self.version_path = app_path / "resources/version.json"
self.download_process_dict: Dict[str, DownloadProcess] = {}
self.timer_dict: Dict[str, QTimer] = {}
self.setWindowTitle("AUTO_MAA更新器")
self.setWindowIcon(
QIcon(str(app_path / "resources/icons/AUTO_MAA_Updater.ico"))
)
self.resize(700, 70)
setTheme(Theme.AUTO, lazy=True)
@@ -539,41 +530,25 @@ class DownloadManager(QDialog):
self.zip_extract.start()
self.zip_loop.exec()
self.update_info("正在删除已弃用的文件")
if (self.app_path / "changes.json").exists():
with (self.app_path / "changes.json").open(mode="r", encoding="utf-8") as f:
info: Dict[str, List[str]] = json.load(f)
if "deleted" in info:
for file_path in info["deleted"]:
if (self.app_path / file_path).exists():
(self.app_path / file_path).unlink()
(self.app_path / "changes.json").unlink()
self.update_info("正在删除临时文件")
self.update_progress(0, 0, 0)
if (self.app_path / "changes.json").exists():
(self.app_path / "changes.json").unlink()
if self.download_path.exists():
self.download_path.unlink()
# 主程序更新完成后打开对应程序
if not self.isInterruptionRequested and self.name == "AUTO_MAA":
subprocess.Popen(
[self.app_path / "AUTO_MAA.exe"],
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
| subprocess.DETACHED_PROCESS
| subprocess.CREATE_NO_WINDOW,
)
elif not self.isInterruptionRequested and self.name == "MAA":
# 下载完成后打开对应程序
if not self.isInterruptionRequested and self.name == "MAA":
subprocess.Popen(
[self.app_path / "MAA.exe"],
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
| subprocess.DETACHED_PROCESS
| subprocess.CREATE_NO_WINDOW,
)
self.update_info(f"{self.name}更新成功!")
if self.name == "AUTO_MAA":
self.update_info(f"即将安装{self.name}")
else:
self.update_info(f"{self.name}下载成功!")
self.update_progress(0, 100, 100)
self.download_accomplish.emit()
@@ -615,142 +590,3 @@ class DownloadManager(QDialog):
self.requestInterruption()
event.accept()
class AUTO_MAA_Downloader(QApplication):
def __init__(
self, app_path: Path, name: str, main_version: list, config: dict
) -> None:
super().__init__()
self.main = DownloadManager(app_path, name, main_version, config)
self.main.show()
self.main.run()
if __name__ == "__main__":
# 获取软件自身的路径
app_path = Path(sys.argv[0]).resolve().parent
# 从本地版本信息文件获取当前版本信息
if (app_path / "resources/version.json").exists():
with (app_path / "resources/version.json").open(
mode="r", encoding="utf-8"
) as f:
current_version_info = json.load(f)
current_version = list(
map(int, current_version_info["main_version"].split("."))
)
else:
current_version = [0, 0, 0, 0]
# 从本地配置文件获取更新信息
if (app_path / "config/config.json").exists():
with (app_path / "config/config.json").open(mode="r", encoding="utf-8") as f:
config = json.load(f)
if "Update" in config:
if "UpdateType" in config["Update"]:
update_type = config["Update"]["UpdateType"]
else:
update_type = "stable"
if "ProxyUrlList" in config["Update"]:
proxy_list = config["Update"]["ProxyUrlList"]
else:
proxy_list = []
if "ThreadNumb" in config["Update"]:
thread_numb = config["Update"]["ThreadNumb"]
else:
thread_numb = 8
if "MirrorChyanCDK" in config["Update"]:
mirrorchyan_CDK = (
win32crypt.CryptUnprotectData(
base64.b64decode(config["Update"]["MirrorChyanCDK"]),
None,
None,
None,
0,
)[1].decode("utf-8")
if config["Update"]["MirrorChyanCDK"]
else ""
)
else:
mirrorchyan_CDK = ""
else:
update_type = "stable"
proxy_list = []
thread_numb = 8
mirrorchyan_CDK = ""
else:
update_type = "stable"
proxy_list = []
thread_numb = 8
mirrorchyan_CDK = ""
# 从远程服务器获取最新版本信息
for _ in range(3):
try:
response = requests.get(
f"https://mirrorchyan.com/api/resources/AUTO_MAA/latest?user_agent=AutoMaaDownloader&current_version={version_text(current_version)}&cdk={mirrorchyan_CDK}&channel={update_type}",
timeout=10,
)
version_info: Dict[str, Union[int, str, Dict[str, str]]] = response.json()
break
except Exception as e:
err = e
time.sleep(0.1)
else:
sys.exit(f"获取版本信息时出错:\n{err}")
if version_info["code"] == 0:
if "url" in version_info["data"]:
download_config = {
"mode": "MirrorChyan",
"thread_numb": 1,
"url": version_info["data"]["url"],
}
else:
download_config = {"mode": "Proxy", "thread_numb": thread_numb}
else:
sys.exit(f"获取版本信息时出错:{version_info["msg"]}")
remote_version = list(
map(
int,
version_info["data"]["version_name"][1:].replace("-beta", "").split("."),
)
)
if download_config["mode"] == "Proxy":
for _ in range(3):
try:
response = requests.get(
"https://gitee.com/DLmaster_361/AUTO_MAA/raw/server/download_info.json",
timeout=10,
)
download_info = response.json()
download_config["proxy_list"] = list(
set(proxy_list + download_info["proxy_list"])
)
download_config["download_dict"] = download_info["download_dict"]
break
except Exception as e:
err = e
time.sleep(0.1)
else:
sys.exit(f"获取代理信息时出错:{err}")
if (app_path / "changes.json").exists():
(app_path / "changes.json").unlink()
# 启动更新线程
if version.parse(version_text(remote_version)) > version.parse(
version_text(current_version)
):
app = AUTO_MAA_Downloader(app_path, "AUTO_MAA", remote_version, download_config)
sys.exit(app.exec())

View File

@@ -28,7 +28,6 @@ v4.3
from loguru import logger
from PySide6.QtWidgets import QApplication, QSystemTrayIcon
from qfluentwidgets import (
qconfig,
Action,
SystemTrayMenu,
SplashScreen,
@@ -182,7 +181,7 @@ class AUTO_MAA(MSFluentWindow):
Action(
FluentIcon.POWER_BUTTON,
"退出主程序",
triggered=lambda: (self.window().close(), QApplication.quit()),
triggered=lambda: System.set_power("KillSelf"),
)
)
@@ -247,10 +246,10 @@ class AUTO_MAA(MSFluentWindow):
# 清理旧日志
self.clean_old_logs()
# 清理临时更新器
if (Config.app_path / "AUTO_Updater.active.exe").exists():
# 清理安装包
if (Config.app_path / "AUTO_MAA-Setup.exe").exists():
try:
(Config.app_path / "AUTO_Updater.active.exe").unlink()
(Config.app_path / "AUTO_MAA-Setup.exe").unlink()
except Exception:
pass

View File

@@ -58,7 +58,7 @@ import shutil
from app.core import Config, MainInfoBar, TaskManager, MaaConfig, MaaUserConfig, Network
from app.services import Crypto
from app.utils import DownloadManager
from .downloader import DownloadManager
from .Widget import (
LineEditMessageBox,
LineEditSettingCard,
@@ -326,21 +326,41 @@ class MemberManager(QWidget):
)
return None
# 从远程服务器获取应用列表
Network.set_info(
mode="get",
url="https://gitee.com/DLmaster_361/AUTO_MAA/raw/server/apps_info.json",
)
Network.start()
Network.loop.exec()
if Network.stutus_code == 200:
apps_info = Network.response_json
else:
logger.warning(f"获取应用列表时出错:{Network.error_message}")
MainInfoBar.push_info_bar(
"warning",
"获取应用列表时出错",
f"网络错误:{Network.stutus_code}",
5000,
)
return None
choice = ComboBoxMessageBox(
self.window(),
"选择一个脚本类型以下载相应脚本",
["选择脚本类型"],
[["MAA", "StarRailAssistant"]],
[list(apps_info.keys())],
)
if choice.exec() and choice.input[0].currentIndex() != -1:
app_name = choice.input[0].currentText()
app_rid = apps_info[app_name]["rid"]
(Config.app_path / f"script/{app_name}").mkdir(parents=True, exist_ok=True)
(Config.app_path / f"script/{app_rid}").mkdir(parents=True, exist_ok=True)
folder = QFileDialog.getExistingDirectory(
self,
f"选择{app_name}下载目录",
str(Config.app_path / f"script/{app_name}"),
str(Config.app_path / f"script/{app_rid}"),
)
if not folder:
logger.warning(f"选择{app_name}下载目录时未选择文件夹")
@@ -349,16 +369,11 @@ class MemberManager(QWidget):
)
return None
if app_name in ["MAA"]:
url = f"https://mirrorchyan.com/api/resources/{app_name}/latest?user_agent=AutoMaaGui&cdk={Crypto.win_decryptor(Config.get(Config.update_MirrorChyanCDK))}&os=win&arch=x64&channel=stable"
elif app_name in ["StarRailAssistant"]:
url = f"https://mirrorchyan.com/api/resources/{app_name}/latest?user_agent=AutoMaaGui&cdk={Crypto.win_decryptor(Config.get(Config.update_MirrorChyanCDK))}&channel=stable"
# 从mirrorc服务器获取最新版本信息
Network.set_info(mode="get", url=url)
Network.set_info(
mode="get",
url=f"https://mirrorchyan.com/api/resources/{app_rid}/latest?user_agent=AutoMaaGui&cdk={Crypto.win_decryptor(Config.get(Config.update_MirrorChyanCDK))}&os={apps_info[app_name]["os"]}&arch={apps_info[app_name]["arch"]}&channel=stable",
)
Network.start()
Network.loop.exec()
if Network.stutus_code == 200:
@@ -415,7 +430,7 @@ class MemberManager(QWidget):
self.downloader = DownloadManager(
Path(folder),
app_name,
app_rid,
None,
{
"mode": "MirrorChyan",

View File

@@ -26,7 +26,8 @@ v4.3
"""
from loguru import logger
from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout
from PySide6.QtWidgets import QWidget, QVBoxLayout
from PySide6.QtGui import QIcon
from PySide6.QtCore import Qt
from qfluentwidgets import (
ScrollArea,
@@ -50,6 +51,7 @@ from typing import Dict, Union
from app.core import Config, MainInfoBar, Network
from app.services import Crypto, System, Notify
from .downloader import DownloadManager
from .Widget import (
SwitchSettingCard,
RangeSettingCard,
@@ -378,45 +380,72 @@ class Setting(QWidget):
else:
all_version_info[key] = value.copy()
version_info = {
"更新总览": f"{main_version_info}\n\n{version_info_markdown(update_version_info)}",
"ALL~版本信息": version_info_markdown(all_version_info),
**{
version_text(list(map(int, k.split(".")))): version_info_markdown(v)
for k, v in version_info_json.items()
},
}
# 询问是否开始版本更新
choice = NoticeMessageBox(self.window(), "版本更新", version_info)
choice = NoticeMessageBox(
self.window(),
"版本更新",
{
"更新总览": f"{main_version_info}\n\n{version_info_markdown(update_version_info)}",
"ALL~版本信息": version_info_markdown(all_version_info),
**{
version_text(
list(map(int, k.split(".")))
): version_info_markdown(v)
for k, v in version_info_json.items()
},
},
)
if choice.exec():
with Config.version_path.open(mode="r", encoding="utf-8") as f:
version_info = json.load(f)
version_info["main_version"] = Config.VERSION
with Config.version_path.open(mode="w", encoding="utf-8") as f:
json.dump(version_info, f, ensure_ascii=False, indent=4)
if (Config.app_path / "AUTO_Updater.exe").exists():
shutil.copy(
Config.app_path / "AUTO_Updater.exe",
Config.app_path / "AUTO_Updater.active.exe",
)
if "url" in version_info["data"]:
download_config = {
"mode": "MirrorChyan",
"thread_numb": 1,
"url": version_info["data"]["url"],
}
else:
logger.error("更新器文件不存在")
MainInfoBar.push_info_bar(
"error", "更新器不存在", "请手动前往 GitHub 获取最新版本", -1
)
return None
subprocess.Popen(
[Config.app_path / "AUTO_Updater.active.exe"],
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
| subprocess.DETACHED_PROCESS
| subprocess.CREATE_NO_WINDOW,
# 从远程服务器获取代理信息
Network.set_info(
mode="get",
url="https://gitee.com/DLmaster_361/AUTO_MAA/raw/server/download_info.json",
)
Network.start()
Network.loop.exec()
if Network.stutus_code == 200:
download_info = Network.response_json
else:
logger.warning(f"获取应用列表时出错:{Network.error_message}")
MainInfoBar.push_info_bar(
"warning",
"获取应用列表时出错",
f"网络错误:{Network.stutus_code}",
5000,
)
return None
download_config = {
"mode": "Proxy",
"thread_numb": Config.get(Config.update_ThreadNumb),
"proxy_list": list(
set(
Config.get(Config.update_ProxyUrlList)
+ download_info["proxy_list"]
)
),
"download_dict": download_info["download_dict"],
}
self.downloader = DownloadManager(
Config.app_path, "AUTO_MAA", remote_version, download_config
)
self.window().close()
QApplication.quit()
self.downloader.setWindowTitle("AUTO_MAA更新器")
self.downloader.setWindowIcon(
QIcon(str(Config.app_path / "resources/icons/AUTO_MAA_Updater.ico"))
)
self.downloader.download_accomplish.connect(self.start_setup)
self.downloader.show()
self.downloader.run()
elif (
if_show
@@ -437,6 +466,23 @@ class Setting(QWidget):
else:
MainInfoBar.push_info_bar("success", "更新检查", "已是最新版本~", 3000)
def start_setup(self) -> None:
subprocess.Popen(
[
Config.app_path / "AUTO_MAA-Setup.exe",
"/SP-",
"/SILENT",
"/NOCANCEL",
"/FORCECLOSEAPPLICATIONS",
"/LANG=Chinese",
f"/DIR={Config.app_path}",
],
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
| subprocess.DETACHED_PROCESS
| subprocess.CREATE_NO_WINDOW,
)
System.set_power("KillSelf")
def show_notice(self, if_show: bool = False, if_first: bool = False) -> None:
"""显示公告"""

View File

@@ -51,7 +51,6 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{
Source: "{#MyAppPath}\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#MyAppPath}\app\*"; DestDir: "{app}\app"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "{#MyAppPath}\resources\*"; DestDir: "{app}\resources"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "{#MyAppPath}\AUTO_Updater.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#MyAppPath}\main.py"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#MyAppPath}\requirements.txt"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#MyAppPath}\README.md"; DestDir: "{app}"; Flags: ignoreversion
@@ -63,7 +62,7 @@ Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall
[Code]
var

View File

@@ -29,6 +29,4 @@ __version__ = "4.2.0"
__author__ = "DLmaster361 <DLmaster_361@163.com>"
__license__ = "GPL-3.0 license"
from .downloader import DownloadManager
__all__ = ["DownloadManager"]
__all__ = []

View File

@@ -86,35 +86,10 @@ if __name__ == "__main__":
print("AUTO_MAA main program packaging completed !")
print("Packaging AUTO_MAA update program ...")
shutil.copy(root_path / "app/utils/downloader.py", root_path)
os.system(
"powershell -Command python -m nuitka --standalone --onefile --mingw64"
" --enable-plugins=pyside6 --windows-console-mode=disable"
" --onefile-tempdir-spec='{TEMP}\\AUTO_MAA_Updater'"
" --windows-icon-from-ico=resources\\icons\\AUTO_MAA_Updater.ico"
" --company-name='AUTO_MAA Team' --product-name=AUTO_MAA"
f" --file-version={version["updater_version"]}"
f" --product-version={version["main_version"]}"
" --file-description='AUTO_MAA Component'"
" --copyright='Copyright © 2024-2025 DLmaster361'"
" --assume-yes-for-downloads --output-filename=AUTO_Updater"
" --remove-output downloader.py"
)
(root_path / "downloader.py").unlink()
print("AUTO_MAA update program packaging completed !")
print("start to create setup program ...")
(root_path / "AUTO_MAA").mkdir(parents=True, exist_ok=True)
print("Start to move AUTO_MAA program ...")
shutil.move(root_path / "AUTO_MAA.exe", root_path / "AUTO_MAA/")
shutil.move(root_path / "AUTO_Updater.exe", root_path / "AUTO_MAA/")
print("Start to copy rescourses ...")
shutil.copytree(root_path / "app", root_path / "AUTO_MAA/app")
shutil.copytree(root_path / "resources", root_path / "AUTO_MAA/resources")
shutil.copy(root_path / "main.py", root_path / "AUTO_MAA/")
@@ -122,7 +97,25 @@ if __name__ == "__main__":
shutil.copy(root_path / "README.md", root_path / "AUTO_MAA/")
shutil.copy(root_path / "LICENSE", root_path / "AUTO_MAA/")
print("Start to compress ...")
with (root_path / "app/utils/AUTO_MAA.iss").open(mode="r", encoding="utf-8") as f:
iss = f.read()
iss = (
iss.replace(
'#define MyAppVersion ""',
f'#define MyAppVersion "{version["main_version"]}"',
)
.replace(
'#define MyAppPath ""', f'#define MyAppPath "{root_path / "AUTO_MAA"}"'
)
.replace('#define OutputDir ""', f'#define OutputDir "{root_path}"')
)
with (root_path / "AUTO_MAA.iss").open(mode="w", encoding="utf-8") as f:
f.write(iss)
os.system(f'ISCC "{root_path / "AUTO_MAA.iss"}"')
(root_path / "AUTO_MAA_Setup").mkdir(parents=True, exist_ok=True)
shutil.move(root_path / "AUTO_MAA-Setup.exe", root_path / "AUTO_MAA_Setup")
shutil.make_archive(
base_name=root_path / f"AUTO_MAA_{version_text(main_version_numb)}",
@@ -130,9 +123,12 @@ if __name__ == "__main__":
root_dir=root_path / "AUTO_MAA",
base_dir=".",
)
shutil.rmtree(root_path / "AUTO_MAA")
print("compress completed !")
print("setup program created !")
(root_path / "AUTO_MAA.iss").unlink(missing_ok=True)
shutil.rmtree(root_path / "AUTO_MAA")
shutil.rmtree(root_path / "AUTO_MAA_Setup")
all_version_info = {}
for v_i in version["version_info"].values():
@@ -146,20 +142,3 @@ if __name__ == "__main__":
f"{version_text(main_version_numb)}\n{version_text(updater_version_numb)}\n<!--{json.dumps(version["version_info"], ensure_ascii=False)}-->\n{version_info_markdown(all_version_info)}",
encoding="utf-8",
)
with (root_path / "app/utils/AUTO_MAA.iss").open(mode="r", encoding="utf-8") as f:
iss = f.read()
iss = (
iss.replace(
'#define MyAppVersion ""',
f'#define MyAppVersion "{version["main_version"]}"',
)
.replace(
'#define MyAppPath ""', f'#define MyAppPath "{root_path / "AUTO_MAA"}"'
)
.replace('#define OutputDir ""', f'#define OutputDir "{root_path}"')
)
with (root_path / "AUTO_MAA.iss").open(mode="w", encoding="utf-8") as f:
f.write(iss)