From 9b775f530ab734fb3c4ecbeb0e7fe05658622b69 Mon Sep 17 00:00:00 2001 From: ikaros <327209194@qq.com> Date: Sun, 17 Mar 2024 23:34:43 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E5=91=BD=E4=BB=A4=E6=9D=BF=E5=9D=97=EF=BC=8C=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0API=E8=B0=83=E7=94=A8=E8=A7=A3=E6=9E=90=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E6=95=B0=E6=8D=AE=EF=BC=8C=E8=83=BD=E5=8A=A8=E6=80=A7?= =?UTF-8?q?=E5=BE=88=E9=AB=98=EF=BC=8C=E6=89=80=E4=BB=A5=E4=B9=9F=E4=BC=9A?= =?UTF-8?q?=E6=9C=89=E7=82=B9=E5=AE=89=E5=85=A8=E9=A3=8E=E9=99=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.json | 108 ++++++++++++++++++++++++++++++++++++++ config.json.bak | 108 ++++++++++++++++++++++++++++++++++++++ utils/common.py | 58 ++++++++++++++++++++- utils/my_handle.py | 127 +++++++++++++++++++++++++++++++++++++++++++++ webui.py | 116 ++++++++++++++++++++++++++++++++++++++++- 5 files changed, 514 insertions(+), 3 deletions(-) diff --git a/config.json b/config.json index 624122ee..c9f3417d 100644 --- a/config.json +++ b/config.json @@ -1147,6 +1147,113 @@ } ] }, + "custom_cmd": { + "enable": false, + "type": "弹幕", + "config": [ + { + "keywords": [ + "天蝎座今日运势", + "天蝎座运势", + "天蝎运势" + ], + "similarity": 1.0, + "api_url": "https://api.vvhan.com/api/horoscope?type=scorpio&time=today", + "api_type": "GET", + "resp_data_type": "json", + "data_analysis": "resp['data']['fortunetext']['all']", + "resp_template": "{keyword}:{data}" + }, + { + "keywords": [ + "随机笑话", + "讲个笑话", + "来个笑话" + ], + "similarity": 1.0, + "api_url": "https://api.vvhan.com/api/joke", + "api_type": "GET", + "resp_data_type": "content", + "data_analysis": "resp", + "resp_template": "{data}" + }, + { + "keywords": [ + "随机情话", + "讲个情话", + "情话" + ], + "similarity": 1.0, + "api_url": "https://api.vvhan.com/api/love", + "api_type": "GET", + "resp_data_type": "content", + "data_analysis": "resp", + "resp_template": "{data}" + }, + { + "keywords": [ + "随机骚话", + "讲个骚话", + "骚话" + ], + "similarity": 1.0, + "api_url": "https://api.vvhan.com/api/sao", + "api_type": "GET", + "resp_data_type": "content", + "data_analysis": "resp", + "resp_template": "{data}" + }, + { + "keywords": [ + "疯狂星期四", + "星期四", + "KFC", + "kfc" + ], + "similarity": 1.0, + "api_url": "https://api.shadiao.pro/kfc", + "api_type": "GET", + "resp_data_type": "json", + "data_analysis": "resp['data']['text']", + "resp_template": "{data}" + }, + { + "keywords": [ + "狗屁不通", + "gpbt" + ], + "similarity": 1.0, + "api_url": "https://oiapi.net/API/Bullshit/?title=oiapi&length=200", + "api_type": "GET", + "resp_data_type": "json", + "data_analysis": "resp['message']", + "resp_template": "{data}" + }, + { + "keywords": [ + "网易热评", + "网易云热评" + ], + "similarity": 1.0, + "api_url": "https://oiapi.net/API/NeteaseHotReviews", + "api_type": "GET", + "resp_data_type": "json", + "data_analysis": "resp['data']['content']", + "resp_template": "{data}" + }, + { + "keywords": [ + "发病" + ], + "similarity": 1.0, + "api_url": "https://oiapi.net/API/SickL/", + "api_type": "GET", + "resp_data_type": "json", + "data_analysis": "resp['message']", + "resp_template": "{data}" + } + ] + }, "translate": { "enable": false, "type": "baidu", @@ -1334,6 +1441,7 @@ "play_audio": true, "web_captions_printer": true, "key_mapping": true, + "custom_cmd": true, "trends_config": true, "abnormal_alarm": true }, diff --git a/config.json.bak b/config.json.bak index 624122ee..c9f3417d 100644 --- a/config.json.bak +++ b/config.json.bak @@ -1147,6 +1147,113 @@ } ] }, + "custom_cmd": { + "enable": false, + "type": "弹幕", + "config": [ + { + "keywords": [ + "天蝎座今日运势", + "天蝎座运势", + "天蝎运势" + ], + "similarity": 1.0, + "api_url": "https://api.vvhan.com/api/horoscope?type=scorpio&time=today", + "api_type": "GET", + "resp_data_type": "json", + "data_analysis": "resp['data']['fortunetext']['all']", + "resp_template": "{keyword}:{data}" + }, + { + "keywords": [ + "随机笑话", + "讲个笑话", + "来个笑话" + ], + "similarity": 1.0, + "api_url": "https://api.vvhan.com/api/joke", + "api_type": "GET", + "resp_data_type": "content", + "data_analysis": "resp", + "resp_template": "{data}" + }, + { + "keywords": [ + "随机情话", + "讲个情话", + "情话" + ], + "similarity": 1.0, + "api_url": "https://api.vvhan.com/api/love", + "api_type": "GET", + "resp_data_type": "content", + "data_analysis": "resp", + "resp_template": "{data}" + }, + { + "keywords": [ + "随机骚话", + "讲个骚话", + "骚话" + ], + "similarity": 1.0, + "api_url": "https://api.vvhan.com/api/sao", + "api_type": "GET", + "resp_data_type": "content", + "data_analysis": "resp", + "resp_template": "{data}" + }, + { + "keywords": [ + "疯狂星期四", + "星期四", + "KFC", + "kfc" + ], + "similarity": 1.0, + "api_url": "https://api.shadiao.pro/kfc", + "api_type": "GET", + "resp_data_type": "json", + "data_analysis": "resp['data']['text']", + "resp_template": "{data}" + }, + { + "keywords": [ + "狗屁不通", + "gpbt" + ], + "similarity": 1.0, + "api_url": "https://oiapi.net/API/Bullshit/?title=oiapi&length=200", + "api_type": "GET", + "resp_data_type": "json", + "data_analysis": "resp['message']", + "resp_template": "{data}" + }, + { + "keywords": [ + "网易热评", + "网易云热评" + ], + "similarity": 1.0, + "api_url": "https://oiapi.net/API/NeteaseHotReviews", + "api_type": "GET", + "resp_data_type": "json", + "data_analysis": "resp['data']['content']", + "resp_template": "{data}" + }, + { + "keywords": [ + "发病" + ], + "similarity": 1.0, + "api_url": "https://oiapi.net/API/SickL/", + "api_type": "GET", + "resp_data_type": "json", + "data_analysis": "resp['message']", + "resp_template": "{data}" + } + ] + }, "translate": { "enable": false, "type": "baidu", @@ -1334,6 +1441,7 @@ "play_audio": true, "web_captions_printer": true, "key_mapping": true, + "custom_cmd": true, "trends_config": true, "abnormal_alarm": true }, diff --git a/utils/common.py b/utils/common.py index 3876bd77..8b3a9858 100644 --- a/utils/common.py +++ b/utils/common.py @@ -905,9 +905,9 @@ def send_request(self, url, method='GET', json_data=None, resp_data_type="json", headers = {'Content-Type': 'application/json'} try: - if method == 'GET': + if method in ['GET', 'get']: response = requests.get(url, headers=headers, timeout=timeout) - elif method == 'POST': + elif method in ['POST', 'post']: response = requests.post(url, headers=headers, data=json.dumps(json_data), timeout=timeout) else: raise ValueError('无效 method. 支持的 methods 为 GET 和 POST.') @@ -920,6 +920,8 @@ def send_request(self, url, method='GET', json_data=None, resp_data_type="json", result = response.json() else: result = response.content + # 使用 'utf-8' 编码来解码字节串 + result = result.decode('utf-8') return result @@ -928,6 +930,58 @@ def send_request(self, url, method='GET', json_data=None, resp_data_type="json", logging.error(f"请求出错: {e}") return None + async def send_async_request(self, url, method='GET', json_data=None, resp_data_type="json", timeout=60): + """ + 发送异步 HTTP 请求并返回结果 + + Parameters: + url (str): 请求的 URL + method (str): 请求方法,'GET' 或 'POST' + json_data (dict): JSON 数据,用于 POST 请求 + resp_data_type (str): 返回数据的类型(json | content) + timeout (int): 请求超时时间 + + Returns: + dict|str: 包含响应的 JSON数据 | 字符串数据 + """ + import aiohttp + + headers = {'Content-Type': 'application/json'} + + try: + # 创建 aiohttp.ClientSession + async with aiohttp.ClientSession() as session: + if method in ['GET', 'get']: + async with session.get(url, headers=headers, timeout=timeout) as response: + # 检查请求是否成功 + response.raise_for_status() + + if resp_data_type == "json": + # 解析响应的 JSON 数据 + result = await response.json() + else: + result = await response.read() + + elif method in ['POST', 'post']: + async with session.post(url, headers=headers, data=json.dumps(json_data), timeout=timeout) as response: + # 检查请求是否成功 + response.raise_for_status() + + if resp_data_type == "json": + # 解析响应的 JSON 数据 + result = await response.json() + else: + result = await response.read() + + else: + raise ValueError('无效 method. 支持的 methods 为 GET 和 POST.') + + return result + + except aiohttp.ClientError as e: + logging.error("请求出错: %s", e) + return None + # 请求web字幕打印机 def send_to_web_captions_printer(self, api_ip_port, data): """请求web字幕打印机 diff --git a/utils/my_handle.py b/utils/my_handle.py index c5f7ad47..a794b8d1 100644 --- a/utils/my_handle.py +++ b/utils/my_handle.py @@ -1705,6 +1705,92 @@ def get_a_copywriting_and_audio_synthesis(key_mapping_config, data): return flag + # 自定义命令处理 + def custom_cmd_handle(self, type, data): + """自定义命令处理 + + Args: + type (str): 数据来源类型(弹幕/回复) + data (dict): 平台侧传入的data数据,直接拿来做解析 + + Returns: + bool: 是否正常触发了自定义命令事件,是True 否False + """ + flag = False + + + try: + if My_handle.config.get("custom_cmd", "enable"): + # 判断传入的数据是否包含gift_name键值,有的话则是礼物数据 + if "gift_name" in data: + pass + else: + username = data["username"] + content = data["content"] + custom_cmd_configs = My_handle.config.get("custom_cmd", "config") + + for custom_cmd_config in custom_cmd_configs: + similarity = float(custom_cmd_config["similarity"]) + for keyword in custom_cmd_config["keywords"]: + if type == "弹幕": + # 判断相似度 + ratio = difflib.SequenceMatcher(None, content, keyword).ratio() + if ratio >= similarity: + resp = My_handle.common.send_request( + custom_cmd_config["api_url"], + custom_cmd_config["api_type"], + resp_data_type=custom_cmd_config["resp_data_type"] + ) + + # 使用 eval() 执行字符串表达式并获取结果 + resp_content = eval(custom_cmd_config["data_analysis"]) + + logging.debug(f"resp_content={resp_content}") + + # 违禁词处理 + resp_content = self.prohibitions_handle(resp_content) + if resp_content is None: + return flag + + variables = { + 'keyword': keyword, + 'cur_time': My_handle.common.get_bj_time(5), + 'username': username, + 'data': resp_content + } + + tmp = custom_cmd_config["resp_template"] + + # 使用字典进行字符串替换 + if any(var in tmp for var in variables): + resp_content = tmp.format(**{var: value for var, value in variables.items() if var in tmp}) + + # 音频合成时需要用到的重要数据 + message = { + "type": "reread", + "tts_type": My_handle.config.get("audio_synthesis_type"), + "data": My_handle.config.get(My_handle.config.get("audio_synthesis_type")), + "config": My_handle.config.get("filter"), + "username": username, + "content": resp_content + } + + logging.debug(message) + + logging.info(f'【触发 自定义命令】关键词:{keyword} 返回内容:{resp_content}') + + self.audio_synthesis_handle(message) + + flag = True + + + except Exception as e: + logging.error(traceback.format_exc()) + logging.error(f'【触发自定义命令】错误:{e}') + + return flag + + """ ,` @@ -1789,6 +1875,12 @@ def comment_handle(self, data): # 按键映射 触发后不执行后面的其他功能 if self.key_mapping_handle("弹幕", data): return + + # 判断自定义命令触发类型 + if My_handle.config.get("custom_cmd", "type") == "弹幕" or My_handle.config.get("custom_cmd", "type") == "弹幕+回复": + # 自定义命令 触发后不执行后面的其他功能 + if self.custom_cmd_handle("弹幕", data): + return try: # 念弹幕 @@ -1939,6 +2031,15 @@ def comment_handle(self, data): if self.key_mapping_handle("回复", data): pass + # 判断自定义命令触发类型 + if My_handle.config.get("custom_cmd", "type") == "回复" or My_handle.config.get("custom_cmd", "type") == "弹幕+回复": + # 替换内容 + data["content"] = resp_content + # 自定义命令 触发后不执行后面的其他功能 + if self.custom_cmd_handle("回复", data): + pass + + # 音频合成时需要用到的重要数据 message = { "type": "comment", @@ -1976,6 +2077,8 @@ def gift_handle(self, data): # 按键映射 触发后仍然执行后面的其他功能 self.key_mapping_handle("弹幕", data) + # 自定义命令触发 + self.custom_cmd_handle("弹幕", data) # 违禁处理 data['username'] = self.prohibitions_handle(data['username']) @@ -2169,6 +2272,12 @@ def idle_time_task_handle(self, data): # 按键映射 触发后不执行后面的其他功能 if self.key_mapping_handle("弹幕", data): return + + # 判断自定义命令触发类型 + if My_handle.config.get("custom_cmd", "type") == "弹幕" or My_handle.config.get("custom_cmd", "type") == "弹幕+回复": + # 自定义命令 触发后不执行后面的其他功能 + if self.custom_cmd_handle("弹幕", data): + return # 1、本地问答库 处理 if self.local_qa_handle(data): @@ -2247,6 +2356,15 @@ def idle_time_task_handle(self, data): if self.key_mapping_handle("回复", data): pass + # 判断自定义命令射触发类型 + if My_handle.config.get("custom_cmd", "type") == "回复" or My_handle.config.get("custom_cmd", "type") == "弹幕+回复": + # 替换内容 + data["content"] = resp_content + # 自定义命令 触发后不执行后面的其他功能 + if self.custom_cmd_handle("回复", data): + pass + + # 音频合成时需要用到的重要数据 message = { "type": "idle_time_task", @@ -2344,6 +2462,15 @@ def image_recognition_schedule_handle(self, data): if self.key_mapping_handle("回复", data): pass + # 判断自定义命令触发类型 + if My_handle.config.get("custom_cmd", "type") == "回复" or My_handle.config.get("custom_cmd", "type") == "弹幕+回复": + # 替换内容 + data["content"] = resp_content + # 自定义命令 触发后不执行后面的其他功能 + if self.custom_cmd_handle("回复", data): + pass + + # 音频合成时需要用到的重要数据 message = { "type": "image_recognition_schedule", diff --git a/webui.py b/webui.py index 7db4275b..454276b4 100644 --- a/webui.py +++ b/webui.py @@ -677,6 +677,60 @@ def trends_copywriting_del(index): logging.error(traceback.format_exc()) + """ + 自定义命令 + """ + + # 自定义命令-增加 + def custom_cmd_add(): + data_len = len(custom_cmd_config_var) + + tmp_config = { + "keywords": [], + "similarity": 1, + "api_url": "", + "api_type": "", + "resp_data_type": "", + "data_analysis": "", + "resp_template": "" + } + + with custom_cmd_config_card.style(card_css): + with ui.row(): + custom_cmd_config_var[str(data_len)] = ui.textarea(label=f"关键词#{int(data_len / 7) + 1}", value=textarea_data_change(tmp_config["keywords"]), placeholder='此处输入触发的关键词,多个请以换行分隔').style("width:200px;") + custom_cmd_config_var[str(data_len + 1)] = ui.input(label=f"相似度#{int(data_len / 7) + 1}", value=tmp_config["similarity"], placeholder='关键词与用户输入的相似度,默认1即100%').style("width:100px;") + custom_cmd_config_var[str(data_len + 2)] = ui.textarea(label=f"API URL#{int(data_len / 7) + 1}", value=tmp_config["api_url"], placeholder='发送HTTP请求的API链接').style("width:300px;") + custom_cmd_config_var[str(data_len + 3)] = ui.select(label=f"API类型#{int(data_len / 7) + 1}", value=tmp_config["api_type"], options={"GET": "GET"}).style("width:100px;") + custom_cmd_config_var[str(data_len + 4)] = ui.select(label=f"请求返回数据类型#{int(data_len / 7) + 1}", value=tmp_config["resp_data_type"], options={"json": "json", "content": "content"}).style("width:150px;") + custom_cmd_config_var[str(data_len + 5)] = ui.textarea(label=f"数据解析(eval执行)#{int(data_len / 7) + 1}", value=tmp_config["data_analysis"], placeholder='数据解析,请不要随意修改resp变量,会被用于最后返回数据内容的解析').style("width:200px;") + custom_cmd_config_var[str(data_len + 6)] = ui.textarea(label=f"返回内容模板#{int(data_len / 7) + 1}", value=tmp_config["resp_template"], placeholder='请不要随意删除data变量,支持动态变量,最终会合并成完成内容进行音频合成').style("width:300px;") + + + # 自定义命令-删除 + def custom_cmd_del(index): + try: + custom_cmd_config_card.remove(int(index) - 1) + # 删除操作 + keys_to_delete = [str(7 * (int(index) - 1) + i) for i in range(7)] + for key in keys_to_delete: + if key in custom_cmd_config_var: + del custom_cmd_config_var[key] + + # 重新编号剩余的键 + updates = {} + for key in sorted(custom_cmd_config_var.keys(), key=int): + new_key = str(int(key) - 7 if int(key) > int(keys_to_delete[-1]) else key) + updates[new_key] = custom_cmd_config_var[key] + + # 应用更新 + custom_cmd_config_var.clear() + custom_cmd_config_var.update(updates) + except Exception as e: + ui.notify(position="top", type="negative", message=f"错误,索引值配置有误:{e}") + logging.error(traceback.format_exc()) + + + """ 配置操作 """ @@ -1002,6 +1056,34 @@ def common_textarea_handle(content): # logging.info(tmp_arr) config_data["key_mapping"]["config"] = tmp_arr + # 自定义命令 + if config.get("webui", "show_card", "common_config", "custom_cmd"): + config_data["custom_cmd"]["enable"] = switch_custom_cmd_enable.value + config_data["custom_cmd"]["type"] = select_custom_cmd_type.value + tmp_arr = [] + # logging.info(custom_cmd_config_var) + for index in range(len(custom_cmd_config_var) // 7): + tmp_json = { + "keywords": [], + "similarity": 1, + "api_url": "", + "api_type": "", + "resp_data_type": "", + "data_analysis": "", + "resp_template": "" + } + tmp_json["keywords"] = common_textarea_handle(custom_cmd_config_var[str(7 * index)].value) + tmp_json["similarity"] = float(custom_cmd_config_var[str(7 * index + 1)].value) + tmp_json["api_url"] = custom_cmd_config_var[str(7 * index + 2)].value + tmp_json["api_type"] = custom_cmd_config_var[str(7 * index + 3)].value + tmp_json["resp_data_type"] = custom_cmd_config_var[str(7 * index + 4)].value + tmp_json["data_analysis"] = custom_cmd_config_var[str(7 * index + 5)].value + tmp_json["resp_template"] = custom_cmd_config_var[str(7 * index + 6)].value + + tmp_arr.append(tmp_json) + # logging.info(tmp_arr) + config_data["custom_cmd"]["config"] = tmp_arr + # 动态配置 if config.get("webui", "show_card", "common_config", "trends_config"): config_data["trends_config"]["enable"] = switch_trends_config_enable.value @@ -1691,6 +1773,7 @@ def common_textarea_handle(content): config_data["webui"]["show_card"]["common_config"]["play_audio"] = switch_webui_show_card_common_config_play_audio.value config_data["webui"]["show_card"]["common_config"]["web_captions_printer"] = switch_webui_show_card_common_config_web_captions_printer.value config_data["webui"]["show_card"]["common_config"]["key_mapping"] = switch_webui_show_card_common_config_key_mapping.value + config_data["webui"]["show_card"]["common_config"]["custom_cmd"] = switch_webui_show_card_common_config_custom_cmd.value config_data["webui"]["show_card"]["common_config"]["trends_config"] = switch_webui_show_card_common_config_trends_config.value config_data["webui"]["show_card"]["common_config"]["abnormal_alarm"] = switch_webui_show_card_common_config_abnormal_alarm.value @@ -2231,7 +2314,36 @@ def common_textarea_handle(content): key_mapping_config_var[str(5 * index + 2)] = ui.textarea(label="按键", value=textarea_data_change(key_mapping_config["keys"]), placeholder='此处输入你要映射的按键,多个按键请以换行分隔(按键名参考pyautogui规则)').style("width:100px;") key_mapping_config_var[str(5 * index + 3)] = ui.input(label="相似度", value=key_mapping_config["similarity"], placeholder='关键词与用户输入的相似度,默认1即100%').style("width:50px;") key_mapping_config_var[str(5 * index + 4)] = ui.textarea(label="文案", value=textarea_data_change(key_mapping_config["copywriting"]), placeholder='此处输入触发后合成的文案内容,多个请以换行分隔').style("width:300px;") - + + if config.get("webui", "show_card", "common_config", "custom_cmd"): + with ui.card().style(card_css): + ui.label('自定义命令') + with ui.row(): + switch_custom_cmd_enable = ui.switch('启用', value=config.get("custom_cmd", "enable")).style(switch_internal_css) + select_custom_cmd_type = ui.select( + label='类型', + options={'弹幕': '弹幕'}, + value=config.get("custom_cmd", "type") + ).style("width:200px") + with ui.row(): + input_custom_cmd_index = ui.input(label='配置索引', value="", placeholder='配置组的排序号,就是说第一个组是1,第二个组是2,以此类推。请填写纯正整数') + button_custom_cmd_add = ui.button('增加配置组', on_click=custom_cmd_add, color=button_internal_color).style(button_internal_css) + button_custom_cmd_del = ui.button('删除配置组', on_click=lambda: custom_cmd_del(input_custom_cmd_index.value), color=button_internal_color).style(button_internal_css) + + custom_cmd_config_var = {} + custom_cmd_config_card = ui.card() + for index, custom_cmd_config in enumerate(config.get("custom_cmd", "config")): + with custom_cmd_config_card.style(card_css): + with ui.row(): + custom_cmd_config_var[str(7 * index)] = ui.textarea(label=f"关键词#{index + 1}", value=textarea_data_change(custom_cmd_config["keywords"]), placeholder='此处输入触发的关键词,多个请以换行分隔').style("width:200px;") + custom_cmd_config_var[str(7 * index + 1)] = ui.input(label=f"相似度#{index + 1}", value=custom_cmd_config["similarity"], placeholder='关键词与用户输入的相似度,默认1即100%').style("width:100px;") + custom_cmd_config_var[str(7 * index + 2)] = ui.textarea(label=f"API URL#{index + 1}", value=custom_cmd_config["api_url"], placeholder='发送HTTP请求的API链接').style("width:300px;") + custom_cmd_config_var[str(7 * index + 3)] = ui.select(label=f"API类型#{index + 1}", value=custom_cmd_config["api_type"], options={"GET": "GET"}).style("width:100px;") + custom_cmd_config_var[str(7 * index + 4)] = ui.select(label=f"请求返回数据类型#{index + 1}", value=custom_cmd_config["resp_data_type"], options={"json": "json", "content": "content"}).style("width:150px;") + custom_cmd_config_var[str(7 * index + 5)] = ui.textarea(label=f"数据解析(eval执行)#{index + 1}", value=custom_cmd_config["data_analysis"], placeholder='数据解析,请不要随意修改resp变量,会被用于最后返回数据内容的解析').style("width:200px;") + custom_cmd_config_var[str(7 * index + 6)] = ui.textarea(label=f"返回内容模板#{index + 1}", value=custom_cmd_config["resp_template"], placeholder='请不要随意删除data变量,支持动态变量,最终会合并成完成内容进行音频合成').style("width:300px;") + + if config.get("webui", "show_card", "common_config", "trends_config"): with ui.card().style(card_css): ui.label('动态配置') @@ -3988,6 +4100,8 @@ def update_echart_gift(): switch_webui_show_card_common_config_play_audio = ui.switch('音频播放', value=config.get("webui", "show_card", "common_config", "play_audio")).style(switch_internal_css) switch_webui_show_card_common_config_web_captions_printer = ui.switch('web字幕打印机', value=config.get("webui", "show_card", "common_config", "web_captions_printer")).style(switch_internal_css) switch_webui_show_card_common_config_key_mapping = ui.switch('按键/文案映射', value=config.get("webui", "show_card", "common_config", "key_mapping")).style(switch_internal_css) + switch_webui_show_card_common_config_custom_cmd = ui.switch('自定义命令', value=config.get("webui", "show_card", "common_config", "custom_cmd")).style(switch_internal_css) + switch_webui_show_card_common_config_trends_config = ui.switch('动态配置', value=config.get("webui", "show_card", "common_config", "trends_config")).style(switch_internal_css) switch_webui_show_card_common_config_abnormal_alarm = ui.switch('异常报警', value=config.get("webui", "show_card", "common_config", "abnormal_alarm")).style(switch_internal_css) with ui.card().style(card_css): From fb4e80276d3428666a9b43e52cddb0945349b8f1 Mon Sep 17 00:00:00 2001 From: ikaros <327209194@qq.com> Date: Mon, 18 Mar 2024 16:28:28 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E6=95=B0=E5=AD=97=E4=BA=BA=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E6=92=AD=E6=94=BE=E5=99=A8=E6=96=B0=E5=A2=9ESadtalker?= =?UTF-8?q?=E3=80=81GeneFace++?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- config.json | 1 + config.json.bak | 1 + utils/audio.py | 1 + webui.py | 8 +++++++- 5 files changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9086d415..3408db95 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ `Luna AI` 是一款结合了最先进技术的虚拟AI主播。它的核心是一系列高效的人工智能模型,包括 `ChatterBot、GPT、Claude、langchain、chatglm、text-generation-webui、讯飞星火、智谱AI、谷歌Bard、文心一言、通义星尘、通义千问、千帆大模型、Gemini、Kimi Chat、QAnything、koboldcpp、FastGPT`。这些模型既可以在本地运行,也可以通过云端服务提供支持。当然,为了让对话照进现实,还结合了多模态模型,包括 `Gemini` 的图像识别能力,获取电脑画面进行分析讲解。 -`Luna AI` 的外观由 `Live2D、Vtube Studio、xuniren、UE5 结合 Audio2Face、EasyAIVtuber、数字人视频播放器(Easy-Wav2Lip)` 技术打造,为用户提供了一个生动、互动的虚拟形象。这使得 `Luna AI` 能够在各大直播平台,如 `Bilibili、抖音、快手、微信视频号、斗鱼、YouTube、Twitch 和 TikTok`,进行实时互动直播。当然,它也可以在本地环境中与您进行个性化对话。 +`Luna AI` 的外观由 `Live2D、Vtube Studio、xuniren、UE5 结合 Audio2Face、EasyAIVtuber、数字人视频播放器(Easy-Wav2Lip、Sadtalker、GeneFace++)` 技术打造,为用户提供了一个生动、互动的虚拟形象。这使得 `Luna AI` 能够在各大直播平台,如 `Bilibili、抖音、快手、微信视频号、斗鱼、YouTube、Twitch 和 TikTok`,进行实时互动直播。当然,它也可以在本地环境中与您进行个性化对话。 为了使交流更加自然,`Luna AI` 使用了先进的自然语言处理技术,结合文本转语音系统,如 `Edge-TTS、VITS-Fast、elevenlabs、bark-gui、VALL-E-X、睿声AI、genshinvoice.top、tts.ai-lab.top、OpenVoice、GPT_SoVITS、clone-voice、Azure TTS、fish-speech`。这不仅让它能够生成流畅的回答,还可以通过 `so-vits-svc 和 DDSP-SVC` 实现声音的变化,以适应不同的场景和角色。 diff --git a/config.json b/config.json index c9f3417d..2af8c57d 100644 --- a/config.json +++ b/config.json @@ -29,6 +29,7 @@ "api_ip_port": "http://127.0.0.1:7888" }, "digital_human_video_player": { + "type": "easy_wav2lip", "api_ip_port": "http://127.0.0.1:8091" }, "play_audio": { diff --git a/config.json.bak b/config.json.bak index c9f3417d..2af8c57d 100644 --- a/config.json.bak +++ b/config.json.bak @@ -29,6 +29,7 @@ "api_ip_port": "http://127.0.0.1:7888" }, "digital_human_video_player": { + "type": "easy_wav2lip", "api_ip_port": "http://127.0.0.1:8091" }, "play_audio": { diff --git a/utils/audio.py b/utils/audio.py index c7fc47f2..81865964 100644 --- a/utils/audio.py +++ b/utils/audio.py @@ -349,6 +349,7 @@ async def digital_human_video_player_api(self, audio_path=""): url = urljoin(self.config.get('digital_human_video_player', 'api_ip_port'), "/show") data = { + "type": self.config.get('digital_human_video_player', 'type'), "audio_path": os.path.abspath(audio_path), "insert_index": -1 } diff --git a/webui.py b/webui.py index 454276b4..b5969d17 100644 --- a/webui.py +++ b/webui.py @@ -1557,6 +1557,7 @@ def common_textarea_handle(content): config_data["EasyAIVtuber"]["api_ip_port"] = input_EasyAIVtuber_api_ip_port.value if config.get("webui", "show_card", "visual_body", "digital_human_video_player"): + config_data["digital_human_video_player"]["type"] = select_digital_human_video_player_type.value config_data["digital_human_video_player"]["api_ip_port"] = input_digital_human_video_player_api_ip_port.value """ @@ -3501,8 +3502,13 @@ def clear_tts_common_audio_card(file_path): if config.get("webui", "show_card", "visual_body", "digital_human_video_player"): with ui.card().style(card_css): - ui.label("digital_human_video_player") + ui.label("数字人视频播放器") with ui.row(): + select_digital_human_video_player_type = ui.select( + label='类型', + options={"easy_wav2lip": "easy_wav2lip", "sadtalker": "sadtalker", "genefaceplusplus": "GeneFacePlusPlus"}, + value=config.get("digital_human_video_player", "type") + ).style("width:150px") input_digital_human_video_player_api_ip_port = ui.input(label='API地址', value=config.get("digital_human_video_player", "api_ip_port"), placeholder='对接 数字人视频播放器 监听的ip和端口')