forked from LmeSzinc/AzurLaneAutoScript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgui.py
More file actions
193 lines (168 loc) · 7.12 KB
/
gui.py
File metadata and controls
193 lines (168 loc) · 7.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import os
import sys # 核心修复:新增sys模块导入
import threading
from multiprocessing import Event, Process, set_start_method
from typing import Optional
from module.logger import logger
from module.webui.setting import State
def func(ev: Optional[Event]): # 修正:改为multiprocessing.Event(Optional适配None传参)
import argparse
import asyncio
import uvicorn
# 修复:macOS下asyncio兼容配置(补充)
if sys.platform == "darwin":
# 禁用fork安全检查,解决Mach端口冲突
os.environ["OBJC_DISABLE_INITIALIZE_FORK_SAFETY"] = "YES"
# macOS下asyncio事件循环兼容
asyncio.set_event_loop_policy(asyncio.DefaultEventLoopPolicy())
elif sys.platform.startswith("win"):
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
State.restart_event = ev
parser = argparse.ArgumentParser(description="Alas web service")
parser.add_argument(
"--host",
type=str,
help="Host to listen. Default to WebuiHost in deploy setting",
)
parser.add_argument(
"-p",
"--port",
type=int,
help="Port to listen. Default to WebuiPort in deploy setting",
)
parser.add_argument(
"-k", "--key", type=str, help="Password of alas. No password by default"
)
parser.add_argument(
"--cdn",
action="store_true",
help="Use jsdelivr cdn for pywebio static files (css, js). Self host cdn by default.",
)
parser.add_argument(
"--electron", action="store_true", help="Runs by electron client."
)
parser.add_argument(
"--ssl-key", dest="ssl_key", type=str, help="SSL key file path for HTTPS support"
)
parser.add_argument(
"--ssl-cert", type=str, help="SSL certificate file path for HTTPS support"
)
parser.add_argument(
"--run",
nargs="+",
type=str,
help="Run alas by config names on startup",
)
args, _ = parser.parse_known_args()
host = args.host or State.deploy_config.WebuiHost or "0.0.0.0"
port = args.port or int(State.deploy_config.WebuiPort) or 22267
ssl_key = args.ssl_key or State.deploy_config.WebuiSSLKey
ssl_cert = args.ssl_cert or State.deploy_config.WebuiSSLCert
ssl = ssl_key is not None and ssl_cert is not None
State.electron = args.electron
logger.hr("Launcher config")
logger.attr("Host", host)
logger.attr("Port", port)
logger.attr("SSL", ssl)
logger.attr("Electron", args.electron)
logger.attr("Reload", ev is not None)
if State.electron:
# https://github.com/LmeSzinc/AzurLaneAutoScript/issues/2051
logger.info("Electron detected, remove log output to stdout")
from module.logger import console_hdlr
logger.removeHandler(console_hdlr)
if ssl_cert is None and ssl_key is not None:
logger.error("SSL key provided without certificate. Please provide both SSL key and certificate.")
elif ssl_key is None and ssl_cert is not None:
logger.error("SSL certificate provided without key. Please provide both SSL key and certificate.")
try: # 新增:捕获uvicorn运行时异常,避免子进程崩溃无日志
if ssl:
uvicorn.run(
"module.webui.app:app",
host=host,
port=port,
factory=True,
ssl_keyfile=ssl_key,
ssl_certfile=ssl_cert
)
else:
uvicorn.run("module.webui.app:app", host=host, port=port, factory=True)
except Exception as e:
logger.error(f"Uvicorn service crashed: {str(e)}")
raise # 抛出异常让父进程感知
# 必须启用自动更新
def update_deploy_auto_update():
"""
自动启用 config/deploy.yaml 中的 AutoUpdate 配置
"""
from pathlib import Path
try:
from module.config.utils import read_file, write_file
deploy_path = Path(__file__).parent / 'config' / 'deploy.yaml'
if deploy_path.exists():
config = read_file(str(deploy_path))
current_auto_update = config.get('Deploy', {}).get('Git', {}).get('AutoUpdate', True)
if current_auto_update != True:
config['Deploy']['Git']['AutoUpdate'] = True
write_file(str(deploy_path), config)
logger.info(f'AutoUpdate enabled: {current_auto_update} -> True')
else:
logger.info('AutoUpdate already enabled')
else:
logger.warning(f'Deploy config not found: {deploy_path}')
except Exception as e:
logger.warning(f'Failed to update AutoUpdate: {e}')
if __name__ == "__main__":
# 自动启用 AutoUpdate 配置
update_deploy_auto_update()
# 核心修复:强制设置multiprocessing启动方式为spawn(解决macOS fork导致的Mach端口崩溃)
try:
# 优先设置spawn,兼容多平台
set_start_method("spawn", force=True)
# macOS下额外添加环境变量,禁用fork安全检查
if os.name == "posix" and sys.platform == "darwin":
os.environ["OBJC_DISABLE_INITIALIZE_FORK_SAFETY"] = "YES"
except RuntimeError:
logger.warning("Failed to set spawn start method, may use fork (not recommended on macOS)")
if State.deploy_config.EnableReload:
should_exit = False
while not should_exit:
event = Event()
process = Process(target=func, args=(event,))
process.start()
logger.info(f"Started Alas web service (PID: {process.pid})")
while not should_exit:
try:
# 等待重启事件(1秒超时,避免阻塞)
restart_triggered = event.wait(1)
except KeyboardInterrupt:
logger.info("Received KeyboardInterrupt, exiting...")
should_exit = True
break
except Exception as e:
logger.error(f"Error waiting for restart event: {str(e)}")
should_exit = True
break
if restart_triggered:
logger.info("Restart event triggered, killing current service...")
process.kill()
process.join(timeout=5) # 新增:等待子进程退出,避免僵尸进程
if process.is_alive():
logger.warning("Failed to kill service process, force exit")
break
elif not process.is_alive():
logger.error("Alas web service exited unexpectedly")
should_exit = True
# 进程仍存活则继续循环
# 确保子进程完全退出
if process.is_alive():
process.terminate()
process.join(timeout=3)
# 最终清理:确保子进程退出
if process.is_alive():
process.kill()
process.join()
logger.info("Alas web service exited successfully")
else:
# 非重启模式直接运行
func(None)