diff --git a/README.md b/README.md index 724a182..c9c9f96 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ Fay是一个完整的开源项目,包含Fay控制器及数字人模型,可 2、以上每个模块可轻易替换成自家核心产品。 -3、本地nlp(rasa+chatglm)的替换方法: +3、本地nlp(rasa+chatglm)的替换方法(https://m.bilibili.com/video/BV1D14y1f7pr?wxfid=o7omF0Vs6RIQFUGAzB6LXOBHa6Yg): 1、安装启动chatglm(github) 2、安装rasa 包:rasa、rasa-sdk 3、进入test/rasa目录启动actions:rasa run actions @@ -115,8 +115,9 @@ Fay是一个完整的开源项目,包含Fay控制器及数字人模型,可 **2023.04:** + 抖音直播互动数据对接更换成系统代理抓包pd解码的方式(运行直播伴侣即可); -+ 提供本地nlp的对接代码(rasa+chatglm)。 -+ 修复若干逻辑及说明错误。 ++ 提供本地nlp的对接代码(rasa+chatglm); ++ 修复若干逻辑及说明错误; ++ 提高抖音字幕监听的稳定性及包兼容性。 **2023.03:** diff --git a/core/viewer.py b/core/viewer.py index c64379f..ce80a93 100644 --- a/core/viewer.py +++ b/core/viewer.py @@ -3,11 +3,6 @@ import random import time import requests -from selenium import webdriver -from selenium.webdriver.common.by import By -from selenium.webdriver.chrome.options import Options -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support.expected_conditions import presence_of_element_located import websocket import ssl @@ -23,6 +18,8 @@ import ssl import websocket + +running = False class WS_Client: def __init__(self, host): self.__ws = None @@ -59,8 +56,8 @@ def on_open(self, ws): pass def __connect(self, host): - websocket.enableTrace(False) - while True: + global running + while running: try: self.__ws = websocket.WebSocketApp(host, on_message=self.on_message, @@ -71,8 +68,7 @@ def __connect(self, host): util.log(1, "弹幕监听WebSocket success.") break except Exception as e: - util.log(1, f"Error connecting: {e}. Retrying in 5 seconds...") - time.sleep(5) + break def close(self): self.__ws.close() @@ -80,65 +76,18 @@ def close(self): class Viewer: - def __init__(self, url): - self.url = url - self.GIFT_TYPES = { - '0ea40b8376ef8157791b928a339ed9c9': (1, '小星星', 1), - 'a29d6cdc0abb7286fdd403915196eaa7': (2, '玫瑰', 1), - '802a21ae29f9fae5abe3693de9f874bd': (3, '抖音', 1), - 'a24b3cc863742fd4bc3de0f53dac4487': (4, '大啤酒', 2), - '4960c39f645d524beda5d50dc372510e': (5, '你最好看', 2), - 'e9b7db267d0501b8963d8000c091e123': (6, '人气票', 1), - '698373dfdac86a90b54facdc38698cbc': (7, '粉丝团灯牌', 1) - } - self.__running = True - self.live_driver = None - self.user_driver = None - self.user_sec_uid = None - self.last_join_data = '' - self.last_interact_datas = [] + def __init__(self): + global running + running = True self.live_started = False - self.last_chat_item_index = 0 self.dy_msg_ws = None - self.exe_process = None def __start(self): MyThread(target=self.__run_dy_msg_ws).start() #获取抖音监听内容 - # MyThread(target=self.__driver_alive_runnable).start()#直播浏览器运行 - self.chrome_options = Options() - chrome_profile_path = "C:/Users/Administrator/AppData/Local/Google/Chrome/User Data"#视实际情况修改 - self.chrome_options.add_argument(f"user-data-dir={chrome_profile_path}") - self.chrome_options.add_argument('--ignore-certificate-errors') - self.chrome_options.add_argument('--allow-insecure-localhost') - self.chrome_options.add_argument('--no-sandbox') # 解决沙箱模式下的限制问题 - self.chrome_options.add_argument('--disable-dev-shm-usage') # 解决共享内存不足的问题 - self.chrome_options.add_argument('--disable-gpu') # 禁用GPU加速 - self.chrome_options.add_argument('--disable-extensions') # 禁用扩展程序 - self.chrome_options.add_argument('--disable-notifications') - self.chrome_options.add_argument('--disable-popup-blocking') - self.chrome_options.add_argument('--disable-infobars') - self.chrome_options.add_argument("--log-level=3") # 只记录错误级别的消息 - - - #隐藏浏览器 - # self.chrome_options.add_argument('--headless') - # self.chrome_options.add_argument('--blink-settings=imagesEnabled=false') - # self.live_driver = webdriver.Chrome(config_util.system_chrome_driver, options=self.chrome_options) - # self.live_driver.set_page_load_timeout(60) - # self.live_driver.get(self.url) - # self.user_driver = webdriver.Chrome(config_util.system_chrome_driver, options=self.chrome_options)#抖音加了验证码,暂时不获取粉丝数 - # self.__wait_live_start()#等待开播 - # self.user_sec_uid = self.__get_render_data(self.live_driver)['app']['initialState']['roomStore']['roomInfo']['room']['owner']['sec_uid'] - # MyThread(target=self.__live_state_runnable).start()#监测直播状态 - # MyThread(target=self.__join_runnable).start()#selenium监测粉丝进入 - # MyThread(target=self.__interact_runnable).start()#selenium监测直播间互动:留言、送礼 - # MyThread(target=self.__follower_runnable).start() #selenium监测粉丝变化 self.live_started = True MyThread(target=self.__get_package_listen_interact_runnable).start() def __run_dy_msg_ws(self): - # exe_path = "./bin/Release_2.85/v2.85.exe" - # self.exe_process = subprocess.Popen([exe_path]) self.dy_msg_ws = WS_Client('ws://127.0.0.1:8888') def start(self): @@ -147,208 +96,12 @@ def start(self): def is_live_started(self): return self.live_started - def __wait_live_start(self): - time.sleep(30) - if self.__is_live(): - return - util.log(1, '等待直播开始...') - time.sleep(30) - while not self.__is_live() and self.__running: - try: - self.live_driver.get(self.url) - except: - pass - time.sleep(30) - - def __is_live(self): - try: - xpath = '//*[@id="_douyin_live_scroll_container_"]/div/div[2]/div/div[2]/div/div[2]/div' - element = self.live_driver.find_element_by_xpath(xpath) - return '结束' not in element.text - except BaseException as e: - print(e) - return False - - def __driver_alive_runnable(self): - while self.__running: - time.sleep(0.1) - try: - if self.live_driver is not None: - try: - self.live_driver.execute_script('javascript:void(0);') - except: - if self.__running: - self.live_driver = webdriver.Chrome(config_util.system_chrome_driver, options=self.chrome_options) - self.live_driver.get(self.url) - if self.user_driver is not None: - try: - self.user_driver.execute_script('javascript:void(0);') - except: - if self.__running: - self.user_driver = webdriver.Chrome(config_util.system_chrome_driver, options=self.chrome_options) - except: - pass - - def __live_state_runnable(self): - while self.__running: - is_live = self.__is_live() - if is_live != self.live_started: - self.live_started = self.__is_live() - self.on_change_state(is_live) - if not is_live: - util.log(1, '直播直播已结束,等待下场直播开始...') - if is_live != True: - try: - self.live_driver.get(self.url) - except: - pass - time.sleep(30) - - def __get_render_data(self, driver): - wait = WebDriverWait(driver, 10) - first_result = wait.until(presence_of_element_located((By.ID, "RENDER_DATA"))) - return json.loads(requests.utils.unquote(first_result.get_attribute("textContent"))) - - def __get_interact_type(self, text): - ary = text.split(':') - if len(ary) >= 2: - content_ary = ary[1].split(' ') - if len(content_ary) == 3 and content_ary[0] == '送出了': - return 3 - return 1 - - def __get_gift_type(self, url): - for gift_id in self.GIFT_TYPES.keys(): - if gift_id in url: - return self.GIFT_TYPES.get(gift_id) - return -1, '其他礼物', 0 - - def __get_join_data(self): - try: - xpath = '//*[@id="_douyin_live_scroll_container_"]/div/div[2]/div/div[2]/div/div[1]/div/div/div/div[1]/div/div[2]' - element = self.live_driver.find_element_by_xpath(xpath) - ary = element.text.split('\n') - text = ary[len(ary) - 1] - if len(text) > 0 and self.last_join_data != text: - self.last_join_data = text - user = text[0:len(text) - 3] - return Interact("live", 2, {"user": user, "msg": "来了"}) - except BaseException as e: - return None - return None - - def __get_interact_data(self): - interact_data = [] - chatroom_xpath = '//*[@id="_douyin_live_scroll_container_"]/div/div[2]/div/div[2]/div/div[1]/div/div/div/div[1]/div/div[1]' - try: - chatroom_element = self.live_driver.find_element_by_xpath(chatroom_xpath) - - index_range = None - - if self.last_chat_item_index < 100: - start = self.last_chat_item_index + 1 - if start < 1: - start = 1 - index_range = range(start, 101) # 升序 - else: - index_range = range(100, 0, -1) # 降序 - - # print("\n上一次: {}".format(self.last_chat_item_index)) - for index in index_range: - - # print("到了: {}".format(index)) - chatroom_item = None - try: - chatroom_item = chatroom_element.find_element_by_xpath(chatroom_xpath + '/div[' + str(index) + ']') - except: - pass - - item_id = None - if self.last_chat_item_index < 100: - if chatroom_item is None: - self.last_chat_item_index = index - 1 - break - elif index >= 100: - self.last_chat_item_index = index - else: - if chatroom_item is None: - continue - item_id = chatroom_item.id - if item_id in self.last_interact_datas: - break - - # print(index) - - if len(self.last_interact_datas) > 200: - self.last_interact_datas.pop(0) - - self.last_interact_datas.append(item_id) - item_text = chatroom_item.text - ary = chatroom_item.text.replace('\r', '').split('\n') - text = ary[len(ary) - 1] - if len(text) < 1 and len(ary) > 1: - text = ary[len(ary) - 2] - speak = self.__get_speak(text) - if speak is None: - # print("无法分析[O]: " + item_text) - # print("无法分析[R]: " + text) - continue - if self.__get_interact_type(text) == 3: - item_msg = None - try: - item_msg = chatroom_element.find_element_by_xpath( - chatroom_xpath + '/div[' + str(index) + ']/div/span[3]/span/span/img') - except: - continue - gift = self.__get_gift_type(item_msg.get_attribute('src')) - arg = speak[1].split(' ') - amount = int(arg[len(arg) - 1]) # 礼物数量 - interact_data.append(Interact("live", 3, { - "user": speak[0], - "msg": ('送出了 {0} X {1}'.format(gift[1], amount)), - "gift": gift, - "amount": amount - })) - else: - interact_data.append(Interact("live", 1, {"user": speak[0], "msg": speak[1]})) - except BaseException as e: - interact_data.reverse() - return interact_data - interact_data.reverse() - return interact_data - - def __get_speak(self, text): - ary = text.split(':') - if len(ary) < 2: - return None - user = ary[0] - speak = text[len(ary[0]) + 1:] - if len(user) > 0 and len(speak) > 0: - return user, speak - - def __join_runnable(self): - while self.__running: - if not self.live_started: - continue - # 进入 抓取 - join_data = self.__get_join_data() - if join_data is not None: - self.on_interact(join_data, time.time()) - time.sleep(0.05) - - def __interact_runnable(self): - while self.__running: - if not self.live_started: - continue - # 发言 & 刷礼物 抓取 - for interact in self.__get_interact_data(): - MyThread(target=self.on_interact, args=[interact, time.time()]).start() - # self.on_interact(interact, time.time()) - #TODO Add by xszyou on 20230412.通过抓包监测互动数据 + #Add by xszyou on 20230412.通过抓包监测互动数据 def __get_package_listen_interact_runnable(self): global interact_datas - while self.__running: + global running + while running: if not self.live_started: continue @@ -356,71 +109,14 @@ def __get_package_listen_interact_runnable(self): MyThread(target=self.on_interact, args=[interact, time.time()]).start() interact_datas.clear() - def __follower_runnable(self): - followers = -1 - while self.__running: - # 关注 抓取 - try: - time.sleep(1.0 + random.random()) - self.user_driver.get(USER_URL + self.user_sec_uid) - time.sleep(0.2) - render_data = self.__get_render_data(self.user_driver) - fs = -1 - for i in range(100, -1, -1): - if str(i) in render_data and 'user' in render_data[str(i)] and 'user' in render_data[str(i)]['user'] and 'followerCount' in render_data[str(i)]['user']['user']: - fs = int(render_data[str(i)]['user']['user']['followerCount']) - break - if fs >= 0: - if self.live_started and 0 < followers < fs: - self.on_interact( - Interact("live", 4, { - "user": "None", - "msg": "粉丝关注" - }), - time.time() - ) - followers = fs - else: - util.log(1, '粉丝数获取异常') - except BaseException as e: - util.log(1, e) - util.log(1, '粉丝数获取异常') - def stop(self): - self.__running = False - if self.live_driver: - self.live_driver.quit() - if self.user_driver: - self.user_driver.quit() + global running + running = False if self.dy_msg_ws: self.dy_msg_ws.close() self.dy_msg_ws = None - # self.disable_windows_proxy() - # subprocess.run(["taskkill", "/F", "/PID", str(self.exe_process.pid)]) - #关闭系统代理 - # def disable_windows_proxy(self): - # settings_key = r'Software\Microsoft\Windows\CurrentVersion\Internet Settings' - # try: - # registry = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) - # settings = winreg.OpenKey(registry, settings_key, 0, winreg.KEY_WRITE) - - # # 设置代理启用值为0(禁用) - # winreg.SetValueEx(settings, 'ProxyEnable', 0, winreg.REG_DWORD, 0) - - # # 清空代理服务器和代理覆盖设置 - # winreg.SetValueEx(settings, 'ProxyServer', 0, winreg.REG_SZ, '') - # winreg.SetValueEx(settings, 'ProxyOverride', 0, winreg.REG_SZ, '') - - # winreg.CloseKey(settings) - # winreg.CloseKey(registry) - - # util.log(1, '系统代理已关闭。') - # except Exception as e: - # print(e) - # util.log(1, '关闭系统代理时出错:', e) - @abstractmethod def on_interact(self, interact, event_time): pass diff --git a/fay_booter.py b/fay_booter.py index a8a39df..5a1cb09 100644 --- a/fay_booter.py +++ b/fay_booter.py @@ -26,8 +26,8 @@ class ViewerListener(Viewer): - def __init__(self, url): - super().__init__(url) + def __init__(self): + super().__init__() def on_interact(self, interact: Interact, event_time): type_names = { @@ -88,7 +88,7 @@ def stop(self): self.paudio.terminate() -#TODO Edit by xszyou on 20230113:录制远程设备音频输入并传给aliyun +#Edit by xszyou on 20230113:录制远程设备音频输入并传给aliyun class DeviceInputListener(Recorder): def __init__(self, fei): super().__init__(fei) @@ -247,7 +247,7 @@ def start(): if liveRoom['enabled']: util.log(1, '开启直播服务...') - viewerListener = ViewerListener(liveRoom['url']) # 监听直播间 + viewerListener = ViewerListener() # 监听直播间 viewerListener.start() if record['enabled']: @@ -255,7 +255,7 @@ def start(): recorderListener = RecorderListener(record['device'], feiFei) # 监听麦克风 recorderListener.start() - #TODO edit by xszyou on 20230113:通过此服务来连接k210、手机等音频输入设备 + #edit by xszyou on 20230113:通过此服务来连接k210、手机等音频输入设备 util.log(1,'开启远程设备音频输入服务...') deviceInputListener = DeviceInputListener(feiFei) # 设备音频输入输出麦克风 deviceInputListener.start() @@ -268,10 +268,9 @@ def start(): -# if __name__ == '__main__': -# ws_server: MyServer = None -# feiFei: FeiFei = None -# viewerListener: Viewer = None -# recorderListener: Recorder = None -# start() -# config_util.save_config() +if __name__ == '__main__': + ws_server: MyServer = None + feiFei: FeiFei = None + viewerListener: Viewer = None + recorderListener: Recorder = None + start() diff --git a/requirements.txt b/requirements.txt index 98e452c..a73015a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ -requests~=2.26.0 -selenium~=4.1.3 +requests~=2.26.0 numpy~=1.22.0 pyaudio~=0.2.11 websockets~=10.2 @@ -11,12 +10,10 @@ pygame~=2.1.2 flask_cors~=3.0.10 PyQtWebEngine~=5.15.5 eyed3~=0.9.6 -websocket~=0.2.1 -websocket-client~=1.3.2 +websocket-client azure-cognitiveservices-speech~=1.21.0 aliyun-python-sdk-core==2.13.3 scipy~=1.10.0 -openai~=0.26.5 simhash pytz gevent~=22.10.1 diff --git a/test/clear_proxy.py b/test/clear_proxy.py new file mode 100644 index 0000000..922be13 --- /dev/null +++ b/test/clear_proxy.py @@ -0,0 +1,27 @@ + +import winreg + + #关闭系统代理 +def disable_windows_proxy(): + settings_key = r'Software\Microsoft\Windows\CurrentVersion\Internet Settings' + try: + registry = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) + settings = winreg.OpenKey(registry, settings_key, 0, winreg.KEY_WRITE) + + # 设置代理启用值为0(禁用) + winreg.SetValueEx(settings, 'ProxyEnable', 0, winreg.REG_DWORD, 0) + + # 清空代理服务器和代理覆盖设置 + winreg.SetValueEx(settings, 'ProxyServer', 0, winreg.REG_SZ, '') + winreg.SetValueEx(settings, 'ProxyOverride', 0, winreg.REG_SZ, '') + + winreg.CloseKey(settings) + winreg.CloseKey(registry) + except Exception as e: + pass + + +if __name__ == '__main__': + disable_windows_proxy() + + \ No newline at end of file