11import 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
63import signal
4+ import sys
75import 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
89142if __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,强制退出。" )
0 commit comments