Skip to content

Commit 1c50012

Browse files
committed
refactor: refactor by D
1 parent e3de39c commit 1c50012

45 files changed

Lines changed: 3262 additions & 1871 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,4 @@ cython_debug/
176176
maim_message/
177177
.vscode/
178178
token.txt
179+
vts_token.txt

config.toml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# VUP-NEXT 配置文件
2+
3+
[general]
4+
# VUP-NEXT 在 MaiCore 中注册的平台标识符
5+
platform_id = "vup_next_dev"
6+
7+
[maicore]
8+
# MaiCore WebSocket 服务器地址
9+
host = "127.0.0.1"
10+
# MaiCore WebSocket 服务器端口
11+
port = 8000
12+
# token = "your_maicore_token_if_needed" # 如果 MaiCore 需要认证,取消注释并设置
13+
14+
[http_server]
15+
# 是否启用本地 HTTP 回调服务器
16+
enable = true
17+
# 监听的主机地址
18+
host = "127.0.0.1"
19+
# 监听的端口
20+
port = 8080
21+
# MaiCore 或其他服务访问的回调路径
22+
callback_path = "/maicore_callback"
23+
24+
25+
[plugins]
26+
enable_console_input = true
27+
enable_tts = true
28+
enable_stt = true
29+
enable_llm_text_precessor = true
30+
enable_prompt_context = true
31+
enable_vtube_studio = true

main.py

Lines changed: 128 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,147 @@
11
import asyncio
2-
from src.sensor.danmaku_live_sensor import danmaku_live_sensor
3-
from src.sensor.danmaku_mock_sensor import danmaku_mock_sensor
4-
from src.utils.logger import get_logger
5-
import sys
2+
import logging
63
import signal
4+
import sys
75
import os
8-
from src.neuro.core import core
9-
import threading
10-
11-
logger = get_logger("main")
12-
13-
14-
def run_danmaku_sensor():
15-
"""在单独线程中运行弹幕传感器"""
16-
loop = asyncio.new_event_loop()
17-
asyncio.set_event_loop(loop)
18-
try:
19-
logger.info("弹幕传感器线程已启动")
20-
loop.run_until_complete(danmaku_live_sensor.connect())
21-
loop.run_forever()
22-
except Exception as e:
23-
logger.error(f"弹幕传感器线程异常: {str(e)}")
24-
finally:
25-
loop.close()
26-
27-
28-
async def boot():
29-
# 启动弹幕传感器线程
30-
# danmaku_thread = threading.Thread(target=run_danmaku_sensor, name="DanmakuSensor")
31-
# danmaku_thread.daemon = True
32-
# danmaku_thread.start()
33-
34-
# 主线程运行核心任务
35-
await asyncio.gather(
36-
core.connect(), # 建立与MaiMaiCore的连接
37-
core.process_input(), # 处理传输给MaiMaiCore的输入
38-
danmaku_mock_sensor.connect(), # 连接弹幕传感器
39-
)
6+
import argparse # 导入 argparse
407

41-
42-
async def halt():
8+
# 尝试导入 tomllib (Python 3.11+), 否则使用 toml
9+
try:
10+
import tomllib
11+
except ModuleNotFoundError:
4312
try:
44-
logger.info("正在关闭系统...")
45-
46-
# 先关闭传感器
47-
# await danmaku_live_sensor.disconnect()
48-
# logger.info("B站直播弹幕传感器已关闭")
13+
import toml as tomllib # type: ignore
14+
except ModuleNotFoundError:
15+
print("错误:需要安装 TOML 解析库。请运行 'pip install toml'", file=sys.stderr)
16+
sys.exit(1)
4917

50-
await danmaku_mock_sensor.disconnect()
51-
logger.info("弹幕模拟传感器已关闭")
18+
# 从 src 目录导入核心类和插件管理器
19+
from src.core.vup_next_core import VupNextCore
20+
from src.core.plugin_manager import PluginManager
5221

53-
# 关闭核心
54-
await core.disconnect()
55-
logger.info("核心已关闭")
22+
# 配置日志 (移到 main 函数内部,根据参数设置)
23+
logger = logging.getLogger("VUP-NEXT-Main") # 获取 logger 实例可以提前
5624

57-
# 取消所有剩余任务
58-
pending_tasks = [task for task in asyncio.all_tasks() if task is not asyncio.current_task()]
25+
# 获取 main.py 文件所在的目录
26+
_BASE_DIR = os.path.dirname(os.path.abspath(__file__))
5927

60-
logger.info(f"正在取消 {len(pending_tasks)} 个剩余任务")
61-
62-
# 打印所有未完成任务的名称,帮助调试
63-
for i, task in enumerate(pending_tasks):
64-
logger.info(f"待取消任务 {i + 1}: {task.get_name()} - {task}")
28+
def load_config(config_filename: str = "config.toml") -> dict:
29+
"""加载位于脚本同目录下的 TOML 配置文件。"""
30+
config_path = os.path.join(_BASE_DIR, config_filename)
31+
logger.debug(f"尝试加载配置文件: {config_path}")
32+
try:
33+
with open(config_path, "rb") as f:
34+
config = tomllib.load(f)
35+
logger.info(f"成功加载配置文件: {config_path}")
36+
return config
37+
except FileNotFoundError:
38+
logger.error(f"错误:配置文件 '{config_path}' 未找到。请确保它在 main.py 文件的同级目录下。")
39+
sys.exit(1)
40+
except tomllib.TOMLDecodeError as e:
41+
logger.error(f"错误:配置文件 '{config_path}' 格式无效: {e}")
42+
sys.exit(1)
43+
except Exception as e:
44+
logger.error(f"加载配置文件 '{config_path}' 时发生未知错误: {e}", exc_info=True)
45+
sys.exit(1)
46+
47+
async def main():
48+
"""应用程序主入口点。"""
49+
50+
# 创建命令行参数解析器
51+
parser = argparse.ArgumentParser(description="VUP-NEXT 应用程序")
52+
# 添加 --debug 参数,用于控制日志级别
53+
parser.add_argument("--debug", action="store_true", help="启用 DEBUG 级别日志输出")
54+
# 解析命令行参数
55+
args = parser.parse_args()
56+
57+
# --- 配置日志 ---
58+
log_level = logging.DEBUG if args.debug else logging.INFO
59+
logging.basicConfig(
60+
level=log_level,
61+
format='%(asctime)s - %(levelname)s - %(name)s - %(message)s',
62+
handlers=[
63+
logging.StreamHandler(sys.stdout) # 默认输出到控制台
64+
]
65+
)
6566

66-
# 给每个任务发送取消信号
67-
for task in pending_tasks:
68-
task.cancel()
67+
logger.info("启动 VUP-NEXT 应用程序...")
68+
if args.debug:
69+
logger.info("已启用 DEBUG 日志级别。")
70+
71+
# --- 加载配置 ---
72+
config = load_config()
73+
74+
# 从配置中提取参数,提供默认值或进行错误处理
75+
general_config = config.get("general", {})
76+
maicore_config = config.get("maicore", {})
77+
http_config = config.get("http_server", {})
78+
79+
platform_id = general_config.get("platform_id", "vup_next_default")
80+
81+
maicore_host = maicore_config.get("host", "127.0.0.1")
82+
maicore_port = maicore_config.get("port", 8000)
83+
# maicore_token = maicore_config.get("token") # 如果需要 token
84+
85+
http_enabled = http_config.get("enable", False)
86+
http_host = http_config.get("host", "127.0.0.1") if http_enabled else None
87+
http_port = http_config.get("port", 8080) if http_enabled else None
88+
http_callback_path = http_config.get("callback_path", "/maicore_callback")
89+
90+
# --- 初始化核心 ---
91+
core = VupNextCore(
92+
platform=platform_id,
93+
maicore_host=maicore_host,
94+
maicore_port=maicore_port,
95+
http_host=http_host, # 如果 http_enabled=False, 这里会是 None
96+
http_port=http_port,
97+
http_callback_path=http_callback_path
98+
# maicore_token=maicore_token # 如果 core 需要 token
99+
)
69100

70-
# 设置超时等待所有任务完成
101+
# --- 插件加载 ---
102+
logger.info("加载插件...")
103+
plugin_manager = PluginManager(core, config.get("plugins", {})) # 传入插件全局配置
104+
# 构建插件目录的绝对或相对路径
105+
# 这里假设 main.py 在 VUP-NEXT 根目录运行
106+
plugin_dir = os.path.join(os.path.dirname(__file__), "src", "plugins")
107+
await plugin_manager.load_plugins(plugin_dir)
108+
logger.info("插件加载完成。")
109+
110+
# --- 连接核心服务 ---
111+
await core.connect() # 连接 WebSocket 并启动 HTTP 服务器
112+
113+
# --- 保持运行并处理退出信号 ---
114+
stop_event = asyncio.Event()
115+
116+
def signal_handler():
117+
logger.info("收到退出信号,开始关闭...")
118+
stop_event.set()
119+
120+
loop = asyncio.get_running_loop()
121+
# 在 Windows 上,SIGINT (Ctrl+C) 通常可用
122+
# 在 Unix/Linux 上,可以添加 SIGTERM
123+
for sig in (signal.SIGINT, signal.SIGTERM):
71124
try:
72-
await asyncio.wait_for(asyncio.gather(*pending_tasks, return_exceptions=True), timeout=2.0)
73-
except asyncio.TimeoutError:
74-
logger.warning("部分任务取消超时,强制退出")
75-
# 打印超时后仍未完成的任务
76-
still_pending = [t for t in pending_tasks if not t.done()]
77-
for i, task in enumerate(still_pending):
78-
logger.warning(f"超时未取消任务 {i + 1}: {task.get_name()} - {task}")
125+
loop.add_signal_handler(sig, signal_handler)
126+
except NotImplementedError:
127+
# Windows 可能不支持 add_signal_handler
128+
# 使用 signal.signal 作为备选方案
129+
signal.signal(sig, lambda signum, frame: signal_handler())
79130

80-
logger.info("所有任务已关闭")
131+
logger.info("应用程序正在运行。按 Ctrl+C 退出。")
132+
await stop_event.wait()
81133

82-
except Exception as e:
83-
logger.error(f"关闭系统失败: {e}")
84-
import traceback
85-
86-
logger.error(traceback.format_exc())
134+
# --- 执行清理 ---
135+
logger.info("正在卸载插件...")
136+
await plugin_manager.unload_plugins() # 在断开连接前卸载插件
87137

138+
logger.info("正在关闭核心服务...")
139+
await core.disconnect()
140+
logger.info("VUP-NEXT 应用程序已关闭。")
88141

89142
if __name__ == "__main__":
90-
# 设置信号处理
91-
loop = asyncio.new_event_loop()
92-
asyncio.set_event_loop(loop)
93-
94143
try:
95-
loop.run_until_complete(boot())
96-
loop.run_forever()
144+
asyncio.run(main())
97145
except KeyboardInterrupt:
98-
logger.warning("收到中断信号,正在关闭...")
99-
loop.run_until_complete(halt())
100-
loop.close()
101-
logger.info("程序已完全退出,强制结束所有进程")
102-
# 强制结束程序
103-
os._exit(0) # 使用os._exit强制退出,不会等待其他线程
104-
except Exception as e:
105-
logger.error(f"主程序异常: {str(e)}")
106-
if loop and not loop.is_closed():
107-
loop.run_until_complete(halt())
108-
loop.close()
109-
os._exit(1)
146+
# 在 asyncio.run 之外捕获 KeyboardInterrupt (尽管上面的信号处理应该先触发)
147+
logger.info("检测到 KeyboardInterrupt,强制退出。")

src/actuator/__init__.py

Lines changed: 0 additions & 4 deletions
This file was deleted.

0 commit comments

Comments
 (0)