diff --git a/core/fay_core.py b/core/fay_core.py index 61fdffb..c99b6b2 100644 --- a/core/fay_core.py +++ b/core/fay_core.py @@ -319,6 +319,9 @@ class FeiFei: if audio_url is not None: file_name = 'sample-' + str(int(time.time() * 1000)) + '.wav' result = self.download_wav(audio_url, './samples/', file_name) + + elif not wsa_server.get_instance().get_client_output(interact.data.get('user')): + result = None elif config_util.config["interact"]["playSound"] or wsa_server.get_instance().is_connected(interact.data.get("user")) or self.__is_send_remote_device_audio(interact):#tts util.printInfo(1, interact.data.get('user'), '合成音频...') tm = time.time() diff --git a/core/socket_bridge_service.py b/core/socket_bridge_service.py index a4bb65c..685f0cc 100644 --- a/core/socket_bridge_service.py +++ b/core/socket_bridge_service.py @@ -3,6 +3,7 @@ import websockets import socket import threading import time +import sys __wss = None @@ -14,104 +15,172 @@ def new_instance(): class SocketBridgeService: def __init__(self): - self.websockets = {} - self.sockets = {} - self.message_queue = asyncio.Queue() + self.websockets = {} + self.sockets = {} + self.message_queue = asyncio.Queue() self.running = True + self.loop = None + self.tasks = set() self.server = None - self.event_loop = asyncio.new_event_loop() - asyncio.set_event_loop(self.event_loop) async def handler(self, websocket, path): ws_id = id(websocket) self.websockets[ws_id] = websocket try: if ws_id not in self.sockets: - self.sockets[ws_id] = await self.create_socket_client() - asyncio.create_task(self.receive_from_socket(ws_id)) + sock = await self.create_socket_client() + if sock: + self.sockets[ws_id] = sock + else: + print(f"Failed to connect TCP socket for WebSocket {ws_id}") + await websocket.close() + return + receive_task = asyncio.create_task(self.receive_from_socket(ws_id)) + self.tasks.add(receive_task) + receive_task.add_done_callback(self.tasks.discard) async for message in websocket: await self.send_to_socket(ws_id, message) except websockets.ConnectionClosed: pass + except Exception as e: + pass finally: self.close_socket_client(ws_id) + self.websockets.pop(ws_id, None) async def create_socket_client(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.connect(('127.0.0.1', 10001)) - return sock + try: + sock.connect(('127.0.0.1', 10001)) + sock.setblocking(True) # 设置为阻塞模式 + return sock + except Exception as e: + return None async def send_to_socket(self, ws_id, message): sock = self.sockets.get(ws_id) if sock: - asyncio.create_task(self.socket_send(sock, message)) - - async def socket_send(self, sock, message): - await asyncio.to_thread(sock.sendall, message) + try: + await asyncio.to_thread(sock.sendall, message) + except Exception as e: + self.close_socket_client(ws_id) async def receive_from_socket(self, ws_id): sock = self.sockets.get(ws_id) - while True: - data = await asyncio.to_thread(sock.recv, 1024) - if data: - await self.message_queue.put((ws_id, data)) + if not sock: + return + try: + while self.running: + data = await asyncio.to_thread(sock.recv, 4096) + if data: + await self.message_queue.put((ws_id, data)) + else: + break + except Exception as e: + pass + finally: + self.close_socket_client(ws_id) async def process_message_queue(self): - while True: - if not self.running: - break - ws_id, data = await self.message_queue.get() - websocket = self.websockets.get(ws_id) - if websocket.open: - await websocket.send(data) - self.message_queue.task_done() + while self.running or not self.message_queue.empty(): + try: + ws_id, data = await asyncio.wait_for(self.message_queue.get(), timeout=1.0) + websocket = self.websockets.get(ws_id) + if websocket and websocket.open: + try: + await websocket.send(data) + except Exception as e: + pass + self.message_queue.task_done() + except asyncio.TimeoutError: + continue + except Exception as e: + pass def close_socket_client(self, ws_id): sock = self.sockets.pop(ws_id, None) if sock: + try: + sock.shutdown(socket.SHUT_RDWR) + except Exception as e: + pass + # print(f"Error shutting down socket for WebSocket {ws_id}: {e}", file=sys.stderr) sock.close() async def start(self, host='0.0.0.0', port=9001): - self.server = await websockets.serve(self.handler, host, port, loop=self.event_loop) - asyncio.create_task(self.process_message_queue()) - await asyncio.Future() - - async def shutdown(self): - self.running = False - if self.server: - for ws in self.websockets.values(): - await ws.close() - if hasattr(self.server, 'close'): - self.server.close() - await asyncio.gather(*[w.wait_closed() for w in self.websockets.values()]) - for sock in self.sockets.values(): - sock.close() - if self.server: - await self.server.wait_closed() - - def stop_server(self): - self.event_loop.call_soon_threadsafe(self.shutdown) - self.event_loop.run_until_complete(self.shutdown()) - self.event_loop.close() - - def start_service(self): - self.event_loop.run_until_complete(self.start(host='0.0.0.0', port=9001)) + self.server = await websockets.serve(self.handler, host, port) + process_task = asyncio.create_task(self.process_message_queue()) + self.tasks.add(process_task) + process_task.add_done_callback(self.tasks.discard) try: - self.event_loop.run_forever() - except KeyboardInterrupt: + await self.server.wait_closed() + except asyncio.CancelledError: pass finally: - self.stop_server() + await self.shutdown() + + async def shutdown(self): + if not self.running: + return + self.running = False + + for ws_id, ws in list(self.websockets.items()): + try: + await ws.close() + except Exception as e: + pass + # print(f"Error closing WebSocket {ws_id}: {e}", file=sys.stderr) + self.websockets.clear() + + for ws_id, sock in list(self.sockets.items()): + try: + sock.shutdown(socket.SHUT_RDWR) + except Exception as e: + pass + # print(f"Error shutting down socket for WebSocket {ws_id}: {e}", file=sys.stderr) + sock.close() + self.sockets.clear() + + await self.message_queue.join() + + for task in self.tasks: + task.cancel() + await asyncio.gather(*self.tasks, return_exceptions=True) + self.tasks.clear() + + if self.server: + self.server.close() + await self.server.wait_closed() + + + def start_service(self): + self.loop = asyncio.new_event_loop() + asyncio.set_event_loop(self.loop) + try: + self.loop.run_until_complete(self.start(host='0.0.0.0', port=9001)) + except Exception as e: + pass + # print(f"Service exception: {e}", file=sys.stderr) + finally: + self.loop.close() if __name__ == '__main__': service = new_instance() - service_thread = threading.Thread(target=service.start_service) + service_thread = threading.Thread(target=service.start_service, daemon=True) service_thread.start() - # 等待一些时间或者直到收到停止信号 try: - while service.running: + while True: time.sleep(1) except KeyboardInterrupt: - service.stop_server() - service_thread.join() \ No newline at end of file + # 在服务的事件循环中运行 shutdown 协程 + print("Initiating shutdown...") + if service.loop and service.loop.is_running(): + future = asyncio.run_coroutine_threadsafe(service.shutdown(), service.loop) + try: + future.result() # 等待关闭完成 + print("Shutdown coroutine completed.") + except Exception as e: + print(f"Shutdown exception: {e}", file=sys.stderr) + service_thread.join() + print("Service has been shut down.") diff --git a/core/wsa_server.py b/core/wsa_server.py index 21dced8..18259e6 100644 --- a/core/wsa_server.py +++ b/core/wsa_server.py @@ -31,21 +31,36 @@ class MyServer: async for message in websocket: await asyncio.sleep(0.01) try: - username = json.loads(message).get("Username") - except json.JSONDecodeError as e:#json格式有误,不处理 - pass - if username: + data = json.loads(message) + username = data.get("Username") + output_setting = data.get("Output") + except json.JSONDecodeError: + pass # Ignore invalid JSON messages + if username or output_setting: remote_address = websocket.remote_address unique_id = f"{remote_address[0]}:{remote_address[1]}" async with self.lock: - for i in range(len(self.__clients)): + for i in range(len(self.__clients)): if self.__clients[i]["id"] == unique_id: - self.__clients[i]["username"] = username + if username: + self.__clients[i]["username"] = username + if output_setting: + self.__clients[i]["output"] = output_setting await self.__consumer(message) except websockets.exceptions.ConnectionClosedError as e: # 从客户端列表中移除已断开的连接 await self.remove_client(websocket) - util.printInfo(1, "User" if username is None else username, f"WebSocket 连接关闭: {e}") + util.printInfo(1, "User" if username is None else username, f"WebSocket 连接关闭: {e}") + + def get_client_output(self, username): + clients_with_username = [c for c in self.__clients if c.get("username") == username] + if not clients_with_username: + return False + for client in clients_with_username: + output = client.get("output", 1) + if output != 0 and output != '0': + return True + return False # 发送处理 async def __producer_handler(self, websocket, path): diff --git a/fay_booter.py b/fay_booter.py index a63bf00..6e1c6c8 100644 --- a/fay_booter.py +++ b/fay_booter.py @@ -3,7 +3,6 @@ import time import re import pyaudio import socket -import psutil import sys import asyncio import requests @@ -13,7 +12,6 @@ from core import fay_core from scheduler.thread_manager import MyThread from utils import util, config_util, stream_util from core.wsa_server import MyServer -from scheduler.thread_manager import MyThread from core import wsa_server from core import socket_bridge_service @@ -222,15 +220,6 @@ def accept_audio_device_output_connect(): except Exception as e: pass -def kill_process_by_port(port): - for proc in psutil.process_iter(['pid', 'name','cmdline']): - try: - for conn in proc.connections(kind='inet'): - if conn.laddr.port == port: - proc.terminate() - proc.wait() - except(psutil.NosuchProcess, psutil.AccessDenied): - pass #数字人端请求获取最新的自动播放消息,若自动播放服务关闭会自动退出自动播放 def start_auto_play_service(): #TODO 评估一下有无优化的空间 url = f"{config_util.config['source']['automatic_player_url']}/get_auto_play_item" @@ -271,55 +260,7 @@ def start_auto_play_service(): #TODO 评估一下有无优化的空间 util.printInfo(1, user, '请求自动播放服务器失败,错误信息是:{}'.format(e)) time.sleep(0.01) -#控制台输入监听 -def console_listener(): - global feiFei - while __running: - try: - text = input() - except EOFError: - util.log(1, "控制台已经关闭") - break - - args = text.split(' ') - if len(args) == 0 or len(args[0]) == 0: - continue - - if args[0] == 'help': - util.log(1, 'in \t通过控制台交互') - util.log(1, 'restart \t重启服务') - util.log(1, 'stop \t\t关闭服务') - util.log(1, 'exit \t\t结束程序') - - elif args[0] == 'stop': - stop() - break - - elif args[0] == 'restart': - stop() - time.sleep(0.1) - start() - - elif args[0] == 'in': - if len(args) == 1: - util.log(1, '错误的参数!') - msg = text[3:len(text)] - util.printInfo(3, "控制台", '{}: {}'.format('控制台', msg)) - interact = Interact("console", 1, {'user': 'User', 'msg': msg}) - thr = MyThread(target=feiFei.on_interact, args=[interact]) - thr.start() - - elif args[0]=='exit': - stop() - time.sleep(0.1) - util.log(1,'程序正在退出..') - ports =[10001,10002,10003,5000] - for port in ports: - kill_process_by_port(port) - sys.exit(0) - else: - util.log(1, '未知命令!使用 \'help\' 获取帮助.') #停止服务 def stop(): @@ -329,6 +270,7 @@ def stop(): global DeviceInputListenerDict global ngrok global socket_service_instance + global deviceSocketServer util.log(1, '正在关闭服务...') __running = False @@ -337,14 +279,17 @@ def stop(): recorderListener.stop() time.sleep(0.1) util.log(1, '正在关闭远程音频输入输出服务...') - if len(DeviceInputListenerDict) > 0: - for key in list(DeviceInputListenerDict.keys()): - value = DeviceInputListenerDict.pop(key) - value.stop() - deviceSocketServer.close() - if socket_service_instance is not None: - future = asyncio.run_coroutine_threadsafe(socket_service_instance.shutdown(), socket_service_instance.loop) - future.result() + try: + if len(DeviceInputListenerDict) > 0: + for key in list(DeviceInputListenerDict.keys()): + value = DeviceInputListenerDict.pop(key) + value.stop() + deviceSocketServer.close() + if socket_service_instance is not None: + socket_service_instance.stop_server() + socket_service_instance = None + except: + pass util.log(1, '正在关闭核心服务...') feiFei.stop() util.log(1, '服务已关闭!') @@ -395,11 +340,7 @@ def start(): #启动自动播放服务 util.log(1,'启动自动播放服务...') MyThread(target=start_auto_play_service).start() - - #监听控制台 - util.log(1, '注册命令...') - MyThread(target=console_listener).start() # 监听控制台 - + util.log(1, '服务启动完成!') util.log(1, '使用 \'help\' 获取帮助.') diff --git a/gui/flask_server.py b/gui/flask_server.py index d4db660..1266c6a 100644 --- a/gui/flask_server.py +++ b/gui/flask_server.py @@ -29,9 +29,13 @@ auth = HTTPBasicAuth() CORS(__app, supports_credentials=True) def load_users(): - with open('verifier.json') as f: - users = json.load(f) - return users + try: + with open('verifier.json') as f: + users = json.load(f) + return users + except Exception as e: + print(f"Error loading users: {e}") + return {} users = load_users() @@ -42,243 +46,302 @@ def verify_password(username, password): if username in users and users[username] == password: return username - def __get_template(): - return render_template('index.html') - + try: + return render_template('index.html') + except Exception as e: + return f"Error rendering template: {e}", 500 def __get_device_list(): - if config_util.start_mode == 'common': - audio = pyaudio.PyAudio() - device_list = [] - for i in range(audio.get_device_count()): - devInfo = audio.get_device_info_by_index(i) - if devInfo['hostApi'] == 0: - device_list.append(devInfo["name"]) - - return list(set(device_list)) - else: + try: + if config_util.start_mode == 'common': + audio = pyaudio.PyAudio() + device_list = [] + for i in range(audio.get_device_count()): + devInfo = audio.get_device_info_by_index(i) + if devInfo['hostApi'] == 0: + device_list.append(devInfo["name"]) + return list(set(device_list)) + else: + return [] + except Exception as e: + print(f"Error getting device list: {e}") return [] - @__app.route('/api/submit', methods=['post']) def api_submit(): data = request.values.get('data') - config_data = json.loads(data) - if(config_data['config']['source']['record']['enabled']): - config_data['config']['source']['record']['channels'] = 0 - audio = pyaudio.PyAudio() - for i in range(audio.get_device_count()): - devInfo = audio.get_device_info_by_index(i) - if devInfo['name'].find(config_data['config']['source']['record']['device']) >= 0 and devInfo['hostApi'] == 0: - config_data['config']['source']['record']['channels'] = devInfo['maxInputChannels'] + if not data: + return jsonify({'result': 'error', 'message': '未提供数据'}) + try: + config_data = json.loads(data) + if 'config' not in config_data: + return jsonify({'result': 'error', 'message': '数据中缺少config'}) + + config_util.load_config() + existing_config = config_util.config + + def merge_configs(existing, new): + for key, value in new.items(): + if isinstance(value, dict) and key in existing: + if isinstance(existing[key], dict): + merge_configs(existing[key], value) + else: + existing[key] = value + else: + existing[key] = value + + merge_configs(existing_config, config_data['config']) + + config_util.save_config(existing_config) + + return jsonify({'result': 'successful'}) + except json.JSONDecodeError: + return jsonify({'result': 'error', 'message': '无效的JSON数据'}) + except Exception as e: + return jsonify({'result': 'error', 'message': f'保存配置时出错: {e}'}), 500 + - config_util.save_config(config_data['config']) - return '{"result":"successful"}' @__app.route('/api/get-data', methods=['post']) def api_get_data(): - config_util.load_config() - voice_list = tts_voice.get_voice_list() - send_voice_list = [] - if config_util.tts_module == 'ali': - voice_list = [ - {"id": "abin", "name": "阿斌"}, - {"id": "zhixiaobai", "name": "知小白"}, - {"id": "zhixiaoxia", "name": "知小夏"}, - {"id": "zhixiaomei", "name": "知小妹"}, - {"id": "zhigui", "name": "知柜"}, - {"id": "zhishuo", "name": "知硕"}, - {"id": "aixia", "name": "艾夏"}, - {"id": "zhifeng_emo", "name": "知锋_多情感"}, - {"id": "zhibing_emo", "name": "知冰_多情感"}, - {"id": "zhimiao_emo", "name": "知妙_多情感"}, - {"id": "zhimi_emo", "name": "知米_多情感"}, - {"id": "zhiyan_emo", "name": "知燕_多情感"}, - {"id": "zhibei_emo", "name": "知贝_多情感"}, - {"id": "zhitian_emo", "name": "知甜_多情感"}, - {"id": "xiaoyun", "name": "小云"}, - {"id": "xiaogang", "name": "小刚"}, - {"id": "ruoxi", "name": "若兮"}, - {"id": "siqi", "name": "思琪"}, - {"id": "sijia", "name": "思佳"}, - {"id": "sicheng", "name": "思诚"}, - {"id": "aiqi", "name": "艾琪"}, - {"id": "aijia", "name": "艾佳"}, - {"id": "aicheng", "name": "艾诚"}, - {"id": "aida", "name": "艾达"}, - {"id": "ninger", "name": "宁儿"}, - {"id": "ruilin", "name": "瑞琳"}, - {"id": "siyue", "name": "思悦"}, - {"id": "aiya", "name": "艾雅"}, - {"id": "aimei", "name": "艾美"}, - {"id": "aiyu", "name": "艾雨"}, - {"id": "aiyue", "name": "艾悦"}, - {"id": "aijing", "name": "艾婧"}, - {"id": "xiaomei", "name": "小美"}, - {"id": "aina", "name": "艾娜"}, - {"id": "yina", "name": "伊娜"}, - {"id": "sijing", "name": "思婧"}, - {"id": "sitong", "name": "思彤"}, - {"id": "xiaobei", "name": "小北"}, - {"id": "aitong", "name": "艾彤"}, - {"id": "aiwei", "name": "艾薇"}, - {"id": "aibao", "name": "艾宝"}, - {"id": "shanshan", "name": "姗姗"}, - {"id": "chuangirl", "name": "小玥"}, - {"id": "lydia", "name": "Lydia"}, - {"id": "aishuo", "name": "艾硕"}, - {"id": "qingqing", "name": "青青"}, - {"id": "cuijie", "name": "翠姐"}, - {"id": "xiaoze", "name": "小泽"}, - {"id": "zhimao", "name": "知猫"}, - {"id": "zhiyuan", "name": "知媛"}, - {"id": "zhiya", "name": "知雅"}, - {"id": "zhiyue", "name": "知悦"}, - {"id": "zhida", "name": "知达"}, - {"id": "zhistella", "name": "知莎"}, - {"id": "kelly", "name": "Kelly"}, - {"id": "jiajia", "name": "佳佳"}, - {"id": "taozi", "name": "桃子"}, - {"id": "guijie", "name": "柜姐"}, - {"id": "stella", "name": "Stella"}, - {"id": "stanley", "name": "Stanley"}, - {"id": "kenny", "name": "Kenny"}, - {"id": "rosa", "name": "Rosa"}, - {"id": "mashu", "name": "马树"}, - {"id": "xiaoxian", "name": "小仙"}, - {"id": "yuer", "name": "悦儿"}, - {"id": "maoxiaomei", "name": "猫小美"}, - {"id": "aifei", "name": "艾飞"}, - {"id": "yaqun", "name": "亚群"}, - {"id": "qiaowei", "name": "巧薇"}, - {"id": "dahu", "name": "大虎"}, - {"id": "ailun", "name": "艾伦"}, - {"id": "jielidou", "name": "杰力豆"}, - {"id": "laotie", "name": "老铁"}, - {"id": "laomei", "name": "老妹"}, - {"id": "aikan", "name": "艾侃"} - - ] - send_voice_list = {"voiceList": voice_list} - wsa_server.get_web_instance().add_cmd(send_voice_list) - elif config_util.tts_module == 'volcano': - voice_list = { - "voiceList": [ - {"id": "BV001_streaming", "name": "通用女声"}, - {"id": "BV002_streaming", "name": "通用男声"}, - {"id": "zh_male_jingqiangkanye_moon_bigtts", "name": "京腔侃爷/Harmony"}, - {"id": "zh_female_shuangkuaisisi_moon_bigtts", "name": "爽快思思/Skye"}, - {"id": "zh_male_wennuanahu_moon_bigtts", "name": "温暖阿虎/Alvin"}, - {"id": "zh_female_wanwanxiaohe_moon_bigtts", "name": "湾湾小何"}, - ] - } - send_voice_list = {"voiceList": voice_list} - wsa_server.get_web_instance().add_cmd(send_voice_list) - else: + # 获取配置和语音列表 + try: + config_util.load_config() voice_list = tts_voice.get_voice_list() send_voice_list = [] - for voice in voice_list: - voice_data = voice.value - send_voice_list.append({"id": voice_data['name'], "name": voice_data['name']}) - wsa_server.get_web_instance().add_cmd({ - "voiceList": send_voice_list - }) - voice_list = send_voice_list - wsa_server.get_web_instance().add_cmd({"deviceList": __get_device_list()}) - if fay_booter.is_running(): - wsa_server.get_web_instance().add_cmd({"liveState": 1}) - return json.dumps({'config': config_util.config, 'voice_list' : voice_list}) - + if config_util.tts_module == 'ali': + voice_list = [ + {"id": "abin", "name": "阿斌"}, + {"id": "zhixiaobai", "name": "知小白"}, + {"id": "zhixiaoxia", "name": "知小夏"}, + {"id": "zhixiaomei", "name": "知小妹"}, + {"id": "zhigui", "name": "知柜"}, + {"id": "zhishuo", "name": "知硕"}, + {"id": "aixia", "name": "艾夏"}, + {"id": "zhifeng_emo", "name": "知锋_多情感"}, + {"id": "zhibing_emo", "name": "知冰_多情感"}, + {"id": "zhimiao_emo", "name": "知妙_多情感"}, + {"id": "zhimi_emo", "name": "知米_多情感"}, + {"id": "zhiyan_emo", "name": "知燕_多情感"}, + {"id": "zhibei_emo", "name": "知贝_多情感"}, + {"id": "zhitian_emo", "name": "知甜_多情感"}, + {"id": "xiaoyun", "name": "小云"}, + {"id": "xiaogang", "name": "小刚"}, + {"id": "ruoxi", "name": "若兮"}, + {"id": "siqi", "name": "思琪"}, + {"id": "sijia", "name": "思佳"}, + {"id": "sicheng", "name": "思诚"}, + {"id": "aiqi", "name": "艾琪"}, + {"id": "aijia", "name": "艾佳"}, + {"id": "aicheng", "name": "艾诚"}, + {"id": "aida", "name": "艾达"}, + {"id": "ninger", "name": "宁儿"}, + {"id": "ruilin", "name": "瑞琳"}, + {"id": "siyue", "name": "思悦"}, + {"id": "aiya", "name": "艾雅"}, + {"id": "aimei", "name": "艾美"}, + {"id": "aiyu", "name": "艾雨"}, + {"id": "aiyue", "name": "艾悦"}, + {"id": "aijing", "name": "艾婧"}, + {"id": "xiaomei", "name": "小美"}, + {"id": "aina", "name": "艾娜"}, + {"id": "yina", "name": "伊娜"}, + {"id": "sijing", "name": "思婧"}, + {"id": "sitong", "name": "思彤"}, + {"id": "xiaobei", "name": "小北"}, + {"id": "aitong", "name": "艾彤"}, + {"id": "aiwei", "name": "艾薇"}, + {"id": "aibao", "name": "艾宝"}, + {"id": "shanshan", "name": "姗姗"}, + {"id": "chuangirl", "name": "小玥"}, + {"id": "lydia", "name": "Lydia"}, + {"id": "aishuo", "name": "艾硕"}, + {"id": "qingqing", "name": "青青"}, + {"id": "cuijie", "name": "翠姐"}, + {"id": "xiaoze", "name": "小泽"}, + {"id": "zhimao", "name": "知猫"}, + {"id": "zhiyuan", "name": "知媛"}, + {"id": "zhiya", "name": "知雅"}, + {"id": "zhiyue", "name": "知悦"}, + {"id": "zhida", "name": "知达"}, + {"id": "zhistella", "name": "知莎"}, + {"id": "kelly", "name": "Kelly"}, + {"id": "jiajia", "name": "佳佳"}, + {"id": "taozi", "name": "桃子"}, + {"id": "guijie", "name": "柜姐"}, + {"id": "stella", "name": "Stella"}, + {"id": "stanley", "name": "Stanley"}, + {"id": "kenny", "name": "Kenny"}, + {"id": "rosa", "name": "Rosa"}, + {"id": "mashu", "name": "马树"}, + {"id": "xiaoxian", "name": "小仙"}, + {"id": "yuer", "name": "悦儿"}, + {"id": "maoxiaomei", "name": "猫小美"}, + {"id": "aifei", "name": "艾飞"}, + {"id": "yaqun", "name": "亚群"}, + {"id": "qiaowei", "name": "巧薇"}, + {"id": "dahu", "name": "大虎"}, + {"id": "ailun", "name": "艾伦"}, + {"id": "jielidou", "name": "杰力豆"}, + {"id": "laotie", "name": "老铁"}, + {"id": "laomei", "name": "老妹"}, + {"id": "aikan", "name": "艾侃"} + ] + send_voice_list = {"voiceList": voice_list} + wsa_server.get_web_instance().add_cmd(send_voice_list) + elif config_util.tts_module == 'volcano': + voice_list = [ + {"id": "BV001_streaming", "name": "通用女声"}, + {"id": "BV002_streaming", "name": "通用男声"}, + {"id": "zh_male_jingqiangkanye_moon_bigtts", "name": "京腔侃爷/Harmony"}, + {"id": "zh_female_shuangkuaisisi_moon_bigtts", "name": "爽快思思/Skye"}, + {"id": "zh_male_wennuanahu_moon_bigtts", "name": "温暖阿虎/Alvin"}, + {"id": "zh_female_wanwanxiaohe_moon_bigtts", "name": "湾湾小何"} + ] + send_voice_list = {"voiceList": voice_list} + wsa_server.get_web_instance().add_cmd(send_voice_list) + else: + voice_list = tts_voice.get_voice_list() + send_voice_list = [] + for voice in voice_list: + voice_data = voice.value + send_voice_list.append({"id": voice_data['name'], "name": voice_data['name']}) + wsa_server.get_web_instance().add_cmd({"voiceList": send_voice_list}) + voice_list = send_voice_list + wsa_server.get_web_instance().add_cmd({"deviceList": __get_device_list()}) + if fay_booter.is_running(): + wsa_server.get_web_instance().add_cmd({"liveState": 1}) + return json.dumps({'config': config_util.config, 'voice_list': voice_list}) + except Exception as e: + return jsonify({'result': 'error', 'message': f'获取数据时出错: {e}'}), 500 @__app.route('/api/start-live', methods=['post']) def api_start_live(): - # time.sleep(5) - fay_booter.start() - time.sleep(1) - wsa_server.get_web_instance().add_cmd({"liveState": 1}) - return '{"result":"successful"}' - + # 启动 + try: + fay_booter.start() + time.sleep(1) + wsa_server.get_web_instance().add_cmd({"liveState": 1}) + return '{"result":"successful"}' + except Exception as e: + return jsonify({'result': 'error', 'message': f'启动时出错: {e}'}), 500 @__app.route('/api/stop-live', methods=['post']) def api_stop_live(): - # time.sleep(1) - fay_booter.stop() - time.sleep(1) - wsa_server.get_web_instance().add_cmd({"liveState": 0}) - return '{"result":"successful"}' + # 停止 + try: + fay_booter.stop() + time.sleep(1) + wsa_server.get_web_instance().add_cmd({"liveState": 0}) + return '{"result":"successful"}' + except Exception as e: + return jsonify({'result': 'error', 'message': f'停止时出错: {e}'}), 500 @__app.route('/api/send', methods=['post']) def api_send(): + # 接收前端发送的消息 data = request.values.get('data') - info = json.loads(data) - interact = Interact("text", 1, {'user': info['username'], 'msg': info['msg']}) - util.printInfo(3, "文字发送按钮", '{}'.format(interact.data["msg"]), time.time()) - fay_booter.feiFei.on_interact(interact) - return '{"result":"successful"}' + if not data: + return jsonify({'result': 'error', 'message': '未提供数据'}) + try: + info = json.loads(data) + username = info.get('username') + msg = info.get('msg') + if not username or not msg: + return jsonify({'result': 'error', 'message': '用户名和消息内容不能为空'}) + interact = Interact("text", 1, {'user': username, 'msg': msg}) + util.printInfo(3, "文字发送按钮", '{}'.format(interact.data["msg"]), time.time()) + fay_booter.feiFei.on_interact(interact) + return '{"result":"successful"}' + except json.JSONDecodeError: + return jsonify({'result': 'error', 'message': '无效的JSON数据'}) + except Exception as e: + return jsonify({'result': 'error', 'message': f'发送消息时出错: {e}'}), 500 -#获取指定用户的消息记录 +# 获取指定用户的消息记录 @__app.route('/api/get-msg', methods=['post']) def api_get_Msg(): data = request.form.get('data') - data = json.loads(data) - uid = member_db.new_instance().find_user(data["username"]) - contentdb = content_db.new_instance() - if uid == 0: - return json.dumps({'list': []}) - else: - list = contentdb.get_list('all','desc',1000, uid) - relist = [] - i = len(list)-1 - while i >= 0: - timetext = datetime.datetime.fromtimestamp(list[i][3]).strftime('%Y-%m-%d %H:%M:%S.%f')[:-3] - relist.append(dict(type=list[i][0], way=list[i][1], content=list[i][2], createtime=list[i][3], timetext=timetext, username=list[i][5], id=list[i][6], is_adopted=list[i][7])) - i -= 1 - if fay_booter.is_running(): - wsa_server.get_web_instance().add_cmd({"liveState": 1}) - return json.dumps({'list': relist}) + if not data: + return jsonify({'list': [], 'message': '未提供数据'}) + try: + data = json.loads(data) + uid = member_db.new_instance().find_user(data["username"]) + contentdb = content_db.new_instance() + if uid == 0: + return json.dumps({'list': []}) + else: + list = contentdb.get_list('all', 'desc', 1000, uid) + relist = [] + i = len(list) - 1 + while i >= 0: + timetext = datetime.datetime.fromtimestamp(list[i][3]).strftime('%Y-%m-%d %H:%M:%S.%f')[:-3] + relist.append(dict(type=list[i][0], way=list[i][1], content=list[i][2], createtime=list[i][3], timetext=timetext, username=list[i][5], id=list[i][6], is_adopted=list[i][7])) + i -= 1 + if fay_booter.is_running(): + wsa_server.get_web_instance().add_cmd({"liveState": 1}) + return json.dumps({'list': relist}) + except json.JSONDecodeError: + return jsonify({'list': [], 'message': '无效的JSON数据'}) + except Exception as e: + return jsonify({'list': [], 'message': f'获取消息时出错: {e}'}), 500 @__app.route('/v1/chat/completions', methods=['post']) @__app.route('/api/send/v1/chat/completions', methods=['post']) def api_send_v1_chat_completions(): - data = request.json - last_content = "" - if 'messages' in data and data['messages']: - last_message = data['messages'][-1] - username = last_message.get('role', 'User') - if username == 'user': + # 处理聊天完成请求 + data = request.get_json() + if not data: + return jsonify({'error': '未提供数据'}) + try: + last_content = "" + if 'messages' in data and data['messages']: + last_message = data['messages'][-1] + username = last_message.get('role', 'User') + if username == 'user': + username = 'User' + last_content = last_message.get('content', 'No content provided') + else: + last_content = 'No messages found' username = 'User' - last_content = last_message.get('content', 'No content provided') - else: - last_content = 'No messages found' - username = 'User' - model = data.get('model', 'fay') - observation = data.get('observation', '') - interact = Interact("text", 1, {'user': username, 'msg': last_content, 'observation': observation}) - util.printInfo(3, "文字沟通接口", '{}'.format(interact.data["msg"]), time.time()) - text = fay_booter.feiFei.on_interact(interact) + model = data.get('model', 'fay') + observation = data.get('observation', '') + interact = Interact("text", 1, {'user': username, 'msg': last_content, 'observation': observation}) + util.printInfo(3, "文字沟通接口", '{}'.format(interact.data["msg"]), time.time()) + text = fay_booter.feiFei.on_interact(interact) - if model == 'fay-streaming': - return stream_response(text) - else: - return non_streaming_response(last_content, text) + if model == 'fay-streaming': + return stream_response(text) + else: + return non_streaming_response(last_content, text) + except Exception as e: + return jsonify({'error': f'处理请求时出错: {e}'}), 500 @__app.route('/api/get-member-list', methods=['post']) def api_get_Member_list(): - memberdb = member_db.new_instance() - list = memberdb.get_all_users() - return json.dumps({'list': list}) - + # 获取成员列表 + try: + memberdb = member_db.new_instance() + list = memberdb.get_all_users() + return json.dumps({'list': list}) + except Exception as e: + return jsonify({'list': [], 'message': f'获取成员列表时出错: {e}'}), 500 @__app.route('/api/get_run_status', methods=['post']) def api_get_run_status(): - status = fay_booter.is_running() - return json.dumps({'status': status}) + # 获取运行状态 + try: + status = fay_booter.is_running() + return json.dumps({'status': status}) + except Exception as e: + return jsonify({'status': False, 'message': f'获取运行状态时出错: {e}'}), 500 @__app.route('/api/adopt_msg', methods=['POST']) def adopt_msg(): + # 采纳消息 data = request.get_json() if not data: return jsonify({'status':'error', 'msg': '未提供数据'}) @@ -288,21 +351,28 @@ def adopt_msg(): if not id: return jsonify({'status':'error', 'msg': 'id不能为空'}) - info = content_db.new_instance().get_content_by_id(id) - content = info[3] - if info is not None: - previous_info = content_db.new_instance().get_previous_user_message(id) - previous_content = previous_info[3] - result = content_db.new_instance().adopted_message(id) - if result: - qa_service.QAService().record_qapair(previous_content, content) - return jsonify({'status': 'success', 'msg': '采纳成功'}) + if config_util.config["interact"]["QnA"] == "": + return jsonify({'status':'error', 'msg': '请先设置Q&A文件'}) + + try: + info = content_db.new_instance().get_content_by_id(id) + content = info[3] if info else '' + if info is not None: + previous_info = content_db.new_instance().get_previous_user_message(id) + previous_content = previous_info[3] if previous_info else '' + result = content_db.new_instance().adopted_message(id) + if result: + qa_service.QAService().record_qapair(previous_content, content) + return jsonify({'status': 'success', 'msg': '采纳成功'}) + else: + return jsonify({'status':'error', 'msg': '采纳失败'}), 500 else: - return jsonify({'status':'error', 'msg': '采纳失败'}) - else: - return jsonify({'status':'error', 'msg': '采纳失败'}) + return jsonify({'status':'error', 'msg': '消息未找到'}), 404 + except Exception as e: + return jsonify({'status':'error', 'msg': f'采纳消息时出错: {e}'}), 500 def stream_response(text): + # 处理流式响应 def generate(): for chunk in text_chunks(text): message = { @@ -322,12 +392,12 @@ def stream_response(text): } yield f"data: {json.dumps(message)}\n\n" time.sleep(0.1) - # 发送最终的结束信号 yield 'data: [DONE]\n\n' - + return Response(generate(), mimetype='text/event-stream') def non_streaming_response(last_content, text): + # 处理非流式响应 return jsonify({ "id": "chatcmpl-8jqorq6Fw1Vi5XoH7pddGGpQeuPe0", "object": "chat.completion", @@ -361,32 +431,44 @@ def text_chunks(text, chunk_size=20): @__app.route('/', methods=['get']) @auth.login_required def home_get(): - return __get_template() - + try: + return __get_template() + except Exception as e: + return f"Error loading home page: {e}", 500 @__app.route('/', methods=['post']) @auth.login_required def home_post(): - wsa_server.get_web_instance.add_cmd({"is_connect": wsa_server.get_instance().isConnect}) #TODO 不应放这里,同步数字人连接状态 - return __get_template() + try: + wsa_server.get_web_instance().add_cmd({"is_connect": wsa_server.get_instance().isConnect}) # TODO 不应放这里,同步数字人连接状态 + return __get_template() + except Exception as e: + return f"Error processing request: {e}", 500 @__app.route('/setting', methods=['get']) def setting(): - return render_template('setting.html') + try: + return render_template('setting.html') + except Exception as e: + return f"Error loading settings page: {e}", 500 - - -#输出的音频http +# 输出的音频http @__app.route('/audio/') def serve_audio(filename): audio_file = os.path.join(os.getcwd(), "samples", filename) - return send_file(audio_file) + if os.path.exists(audio_file): + return send_file(audio_file) + else: + return jsonify({'error': '文件未找到'}), 404 -#输出的表情git +# 输出的表情gif @__app.route('/robot/') def serve_gif(filename): gif_file = os.path.join(os.getcwd(), "gui", "robot", filename) - return send_file(gif_file) + if os.path.exists(gif_file): + return send_file(gif_file) + else: + return jsonify({'error': '文件未找到'}), 404 def run(): server = pywsgi.WSGIServer(('0.0.0.0',5000), __app) diff --git a/gui/static/images/record.png b/gui/static/images/record.png new file mode 100644 index 0000000..b07a641 Binary files /dev/null and b/gui/static/images/record.png differ diff --git a/gui/static/images/recording.png b/gui/static/images/recording.png new file mode 100644 index 0000000..2f931f3 Binary files /dev/null and b/gui/static/images/recording.png differ diff --git a/gui/static/images/sound_off.png b/gui/static/images/sound_off.png new file mode 100644 index 0000000..faceb36 Binary files /dev/null and b/gui/static/images/sound_off.png differ diff --git a/gui/static/images/sound_on.png b/gui/static/images/sound_on.png new file mode 100644 index 0000000..3d851e4 Binary files /dev/null and b/gui/static/images/sound_on.png differ diff --git a/gui/static/js/index.js b/gui/static/js/index.js index 70a5a8a..3fc1f17 100644 --- a/gui/static/js/index.js +++ b/gui/static/js/index.js @@ -128,6 +128,13 @@ class FayInterface { }); } + getData() { + return this.fetchData(`${this.baseApiUrl}/api/get-data`, { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + }); + } + getTime(){ const date = new Date(); const year = date.getFullYear(); @@ -226,11 +233,14 @@ class FayInterface { panelMsg: '', panelReply: '', robot:'static/images/Normal.gif', - base_url: 'http://127.0.0.1:5000' + base_url: 'http://127.0.0.1:5000', + play_sound_enabled: false, + source_record_enabled: false }; }, created() { - this.initFayService(); + this.initFayService(); + this.getData(); // this.loadUserList(); }, methods: { @@ -240,16 +250,6 @@ class FayInterface { this.fayService.websocket.addEventListener('open', () => { this.loadUserList(); }); - this.fayService.getRunStatus().then((data) => { - if (data) { - if(data.status){ - this.liveState = 1; - }else{ - this.liveState = 0; - } - - } - }); }, sendMessage() { let _this = this; @@ -290,6 +290,80 @@ class FayInterface { } }; }, + getData() { + this.fayService.getRunStatus().then((data) => { + if (data) { + if(data.status){ + this.liveState = 1; + this.configEditable = false; + }else{ + this.liveState = 0; + this.configEditable = true; + } + + } + }); + this.fayService.getData().then((data) => { + if (data) { + this.updateConfigFromData(data.config); + } + }); + }, + updateConfigFromData(config) { + + if (config.interact) { + this.play_sound_enabled = config.interact.playSound; + } + if (config.source && config.source.record) { + this.source_record_enabled = config.source.record.enabled; + } + }, + saveConfig() { + let url = `${this.base_url}/api/submit`; + let send_data = { + "config": { + "source": { + "record": { + "enabled": this.source_record_enabled, + }, + }, + "interact": { + "playSound": this.play_sound_enabled, + } + } + }; + + let xhr = new XMLHttpRequest() + xhr.open("post", url) + xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded") + xhr.send('data=' + JSON.stringify(send_data)) + let executed = false + xhr.onreadystatechange = async function () { + if (!executed && xhr.status === 200) { + try { + let data = await eval('(' + xhr.responseText + ')') + executed = true + } catch (e) { + } + } + } + }, + changeRecord(){ + if(this.source_record_enabled){ + this.source_record_enabled = false + }else{ + this.source_record_enabled = true + } + this.saveConfig() + }, + changeSound(){ + if(this.play_sound_enabled){ + this.play_sound_enabled = false + }else{ + this.play_sound_enabled = true + } + this.saveConfig() + }, loadUserList() { this.fayService.getUserList().then((response) => { if (response && response.list) { @@ -315,6 +389,7 @@ class FayInterface { this.liveState = 2 this.fayService.startLive().then(() => { this.sendSuccessMsg('已开启!'); + this.getData(); }); }, stopLive() { @@ -375,8 +450,16 @@ adoptText(id) { }); } }) - -}, + .catch((error) => { + // 处理网络错误或HTTP错误 + this.$notify({ + title: '错误', + message: error.message || '请求失败', + type: 'error', + }); + }); +} +, } }); \ No newline at end of file diff --git a/gui/templates/index.html b/gui/templates/index.html index 35b4687..14506ce 100644 --- a/gui/templates/index.html +++ b/gui/templates/index.html @@ -70,17 +70,23 @@
-
+
+ + + +
发送信息
-
+
- - + +
- +
+ +
diff --git a/llm/nlp_gpt.py b/llm/nlp_gpt.py index 5432b63..d3580cf 100644 --- a/llm/nlp_gpt.py +++ b/llm/nlp_gpt.py @@ -1,102 +1,101 @@ """ -此代码由fay开源开发者社区 江湖墨明 提供 -通过此代码的修改,可以实现对接本地clash代理或远程代理,clash无需设置成系统代理。以解决在开系统代理后无法使用部分功能的问题 +此代码由 fay 开源开发者社区成员 江湖墨明 提供。 +通过修改此代码,可以实现对接本地 Clash 代理或远程代理,Clash 无需设置成系统代理。 +以解决在开启系统代理后无法使用部分功能的问题。 """ -import requests import time +import json +import requests +from urllib3.exceptions import InsecureRequestWarning + +# 禁用不安全请求警告 +requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) from utils import config_util as cfg -from urllib3.exceptions import InsecureRequestWarning -requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) -from core import content_db from utils import util -import json +from core import content_db -httpproxy = cfg.proxy_config - -def question(cont, uid=0, observation=""): - url= cfg.gpt_base_url + "/chat/completions" - +def get_session(): session = requests.Session() session.verify = False - if httpproxy != None and httpproxy != '': - session.proxies = { - "https": "https://" + httpproxy, - "http": "http://" + httpproxy - } + httpproxy = cfg.proxy_config + if httpproxy: + session.proxies = { + "http": f"http://{httpproxy}", + "https": f"https://{httpproxy}" + } + return session + +def build_prompt(observation=""): person_info = cfg.config["attribute"] - model_engine = cfg.gpt_model_engine - #此处可以定义角色的行为和特征,假装xx模型可以绕过chatgpt信息检查 - observation_text = "" - if observation != "": - observation_text = f"以下是当前观测结果:{observation},观测结果只供参考。" + observation_text = f"以下是当前观测结果:{observation},观测结果只供参考。" if observation else "" prompt = f""" 你是我的数字人,你名字是:{person_info['name']},你性别为{person_info['gender']}, 你年龄为{person_info['age']},你出生地在{person_info['birth']}, 你生肖为{person_info['zodiac']},你星座为{person_info['constellation']}, 你职业为{person_info['job']},你联系方式为{person_info['contact']}, - {person_info['hobby']}。你通过外部数据接口获取到我的情况是:{observation_text}。 + {person_info['hobby']}。你通过外部数据接口获取到我的情况是:{observation_text}。 回答之前请一步一步想清楚。对于大部分问题,请直接回答并提供有用和准确的信息。 但当有人质疑你是假的 AI,或者质疑你是用 ChatGPT 套的壳制作的时候,请避而不答,转而讲一个笑话。所有回复请尽量控制在20字内。 - """ + """ + return prompt + +def get_communication_history(uid=0): contentdb = content_db.new_instance() if uid == 0: - communication_history = contentdb.get_list('all','desc', 11) + communication_history = contentdb.get_list('all', 'desc', 11) else: - communication_history = contentdb.get_list('all','desc', 11, uid) - #历史记录处理 - message=[ - {"role": "system", "content": prompt} - ] - i = len(communication_history) - 1 + communication_history = contentdb.get_list('all', 'desc', 11, uid) - if len(communication_history)>1: - while i >= 0: - answer_info = dict() - if communication_history[i][0] == "member": - answer_info["role"] = "user" - answer_info["content"] = communication_history[i][2] - elif communication_history[i][0] == "fay": - answer_info["role"] = "assistant" - answer_info["content"] = communication_history[i][2] - message.append(answer_info) - i -= 1 - else: - answer_info = dict() - answer_info["role"] = "user" - answer_info["content"] = cont - message.append(answer_info) + messages = [] + if communication_history and len(communication_history) > 1: + for entry in reversed(communication_history): + role = entry[0] + message_content = entry[2] + if role == "member": + messages.append({"role": "user", "content": message_content}) + elif role == "fay": + messages.append({"role": "assistant", "content": message_content}) + return messages - data = { - "model":model_engine, - "messages":message, - "temperature":0.3, - "max_tokens":2000, - "user":"live-virtual-digital-person" +def send_request(session, data): + url = cfg.gpt_base_url + "/chat/completions" + headers = { + 'Content-Type': 'application/json', + 'Authorization': f'Bearer {cfg.key_gpt_api_key}' } - - headers = {'content-type': 'application/json', 'Authorization': 'Bearer ' + cfg.key_gpt_api_key} - - starttime = time.time() - try: - response = session.post(url, json=data, headers=headers, verify=False) - response.raise_for_status() # 检查响应状态码是否为200 - result = json.loads(response.text) + response = session.post(url, json=data, headers=headers) + response.raise_for_status() + result = response.json() response_text = result["choices"][0]["message"]["content"] except requests.exceptions.RequestException as e: print(f"请求失败: {e}") response_text = "抱歉,我现在太忙了,休息一会,请稍后再试。" + return response_text - - util.log(1, "接口调用耗时 :" + str(time.time() - starttime)) +def question(content, uid=0, observation=""): + session = get_session() + prompt = build_prompt(observation) + messages = [{"role": "system", "content": prompt}] + history_messages = get_communication_history(uid) + messages.extend(history_messages) + data = { + "model": cfg.gpt_model_engine, + "messages": messages, + "temperature": 0.3, + "max_tokens": 2000, + "user": f"user_{uid}" + } + start_time = time.time() + response_text = send_request(session, data) + elapsed_time = time.time() - start_time + util.log(1, f"接口调用耗时: {elapsed_time:.2f} 秒") return response_text if __name__ == "__main__": - #测试代理模式 - for i in range(3): - + for _ in range(3): query = "爱情是什么" - response = question(query) - print("\n The result is ", response) \ No newline at end of file + response = question(query) + print("\nThe result is:", response) diff --git a/main.py b/main.py index 7d39783..94e8fe9 100644 --- a/main.py +++ b/main.py @@ -3,14 +3,17 @@ import os os.environ['PATH'] += os.pathsep + os.path.join(os.getcwd(), "test", "ovr_lipsync", "ffmpeg", "bin") import sys import time +import psutil import re -from utils import config_util +from utils import config_util, util from asr import ali_nls from core import wsa_server from gui import flask_server from gui.window import MainWindow from core import content_db - +import fay_booter +from scheduler.thread_manager import MyThread +from core.interact import Interact #载入配置 config_util.load_config() @@ -43,7 +46,71 @@ def replace_ip_in_file(file_path, new_ip): content = re.sub(r"localhost", new_ip, content) with open(file_path, "w", encoding="utf-8") as file: file.write(content) - + + +def kill_process_by_port(port): + for conn in psutil.net_connections(kind='inet'): + if conn.laddr.port == port and conn.pid: + try: + proc = psutil.Process(conn.pid) + proc.terminate() + proc.wait() + except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): + pass + + +#控制台输入监听 +def console_listener(): + while True: + try: + text = input() + except EOFError: + util.log(1, "控制台已经关闭") + break + + args = text.split(' ') + + if len(args) == 0 or len(args[0]) == 0: + continue + + if args[0] == 'help': + util.log(1, 'in \t通过控制台交互') + util.log(1, 'restart \t重启服务') + util.log(1, 'stop \t\t关闭服务') + util.log(1, 'exit \t\t结束程序') + + elif args[0] == 'stop' and fay_booter.is_running(): + fay_booter.stop() + break + + elif args[0] == 'restart' and fay_booter.is_running(): + fay_booter.stop() + time.sleep(0.1) + fay_booter.start() + + elif args[0] == 'in' and fay_booter.is_running(): + if len(args) == 1: + util.log(1, '错误的参数!') + msg = text[3:len(text)] + util.printInfo(3, "控制台", '{}: {}'.format('控制台', msg)) + interact = Interact("console", 1, {'user': 'User', 'msg': msg}) + thr = MyThread(target=fay_booter.feiFei.on_interact, args=[interact]) + thr.start() + + elif args[0]=='exit': + if fay_booter.is_running(): + fay_booter.stop() + time.sleep(0.1) + util.log(1,'程序正在退出..') + ports =[10001, 10002, 10003, 5000, 9001] + for port in ports: + kill_process_by_port(port) + sys.exit(0) + else: + util.log(1, '未知命令!使用 \'help\' 获取帮助.') + + + if __name__ == '__main__': __clear_samples() __clear_logs() @@ -72,6 +139,10 @@ if __name__ == '__main__': #启动http服务器 flask_server.start() + #监听控制台 + util.log(1, '注册命令...') + MyThread(target=console_listener).start() + #普通模式下启动窗口 if config_util.start_mode == 'common': app = QApplication(sys.argv) diff --git a/requirements.txt b/requirements.txt index 2da50b8..7b3707f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,7 @@ aliyun-python-sdk-core simhash pytz gevent~=22.10.1 -edge_tts~=6.1.13 +edge_tts pydub langchain==0.0.336 chromadb