diff --git a/astrbot/core/platform/sources/gewechat/client.py b/astrbot/core/platform/sources/gewechat/client.py index 53ee1878e..499bc76b8 100644 --- a/astrbot/core/platform/sources/gewechat/client.py +++ b/astrbot/core/platform/sources/gewechat/client.py @@ -499,6 +499,23 @@ class SimpleGewechatClient: json_blob = await resp.json() logger.debug(f"发送图片结果: {json_blob}") + async def post_video( + self, to_wxid, video_url: str, thumb_url: str, video_duration: int + ): + payload = { + "appId": self.appid, + "toWxid": to_wxid, + "videoUrl": video_url, + "thumbUrl": thumb_url, + "videoDuration": video_duration, + } + async with aiohttp.ClientSession() as session: + async with session.post( + f"{self.base_url}/message/postVideo", headers=self.headers, json=payload + ) as resp: + json_blob = await resp.json() + logger.debug(f"发送视频结果: {json_blob}") + async def post_voice(self, to_wxid, voice_url: str, voice_duration: int): """发送语音信息 diff --git a/astrbot/core/platform/sources/gewechat/gewechat_event.py b/astrbot/core/platform/sources/gewechat/gewechat_event.py index 3aca64bab..5b74a63eb 100644 --- a/astrbot/core/platform/sources/gewechat/gewechat_event.py +++ b/astrbot/core/platform/sources/gewechat/gewechat_event.py @@ -2,12 +2,13 @@ import wave import uuid import traceback import os + from astrbot.core.utils.io import save_temp_img, download_file from astrbot.core.utils.tencent_record_helper import wav_to_tencent_silk from astrbot.api import logger from astrbot.api.event import AstrMessageEvent, MessageChain from astrbot.api.platform import AstrBotMessage, PlatformMetadata, Group, MessageMember -from astrbot.api.message_components import Plain, Image, Record, At, File +from astrbot.api.message_components import Plain, Image, Record, At, File, Video from .client import SimpleGewechatClient @@ -82,6 +83,56 @@ class GewechatPlatformEvent(AstrMessageEvent): img_url = f"{client.file_server_url}/{file_id}" logger.debug(f"gewe callback img url: {img_url}") await client.post_image(to_wxid, img_url) + elif isinstance(comp, Video): + try: + from pyffmpeg import FFmpeg + except (ImportError, ModuleNotFoundError): + logger.error( + "需要安装 pyffmpeg 库才能发送视频: pip install pyffmpeg" + ) + raise ModuleNotFoundError( + "需要安装 pyffmpeg 库才能发送视频: pip install pyffmpeg" + ) + + video_url = comp.file + # 根据 url 下载视频 + video_filename = f"{uuid.uuid4()}.mp4" + video_path = f"data/temp/{video_filename}" + await download_file(video_url, video_path) + + # 获取视频第一帧 + thumb_path = f"data/temp/{uuid.uuid4()}.jpg" + try: + ff = FFmpeg() + command = f'-i "{video_path}" -ss 0 -vframes 1 "{thumb_path}"' + ff.options(command) + thumb_file_id = os.path.basename(thumb_path) + thumb_url = f"{client.file_server_url}/{thumb_file_id}" + except Exception as e: + logger.error(f"获取视频第一帧失败: {e}") + # 获取视频时长 + try: + from pyffmpeg import FFprobe + + # 创建 FFprobe 实例 + ffprobe = FFprobe(video_url) + # 获取时长字符串 + duration_str = ffprobe.duration + # 处理时长字符串 + video_duration = float(duration_str.replace(":", "")) + except Exception as e: + logger.error(f"获取时长失败: {e}") + video_duration = 10 + + file_id = os.path.basename(video_path) + video_url = f"{client.file_server_url}/{file_id}" + await client.post_video(to_wxid, video_url, thumb_url, video_duration) + + # 删除临时视频和缩略图文件 + if os.path.exists(video_path): + os.remove(video_path) + if os.path.exists(thumb_path): + os.remove(thumb_path) elif isinstance(comp, Record): # 默认已经存在 data/temp 中 record_url = comp.file diff --git a/dashboard/src/components/shared/AstrBotConfig.vue b/dashboard/src/components/shared/AstrBotConfig.vue index 2796f95da..2e24af80a 100644 --- a/dashboard/src/components/shared/AstrBotConfig.vue +++ b/dashboard/src/components/shared/AstrBotConfig.vue @@ -1,156 +1,374 @@ \ No newline at end of file + + + \ No newline at end of file