This commit is contained in:
雅诺狐
2025-08-17 14:17:46 +08:00
4 changed files with 95 additions and 1 deletions

View File

@@ -24,7 +24,7 @@
- [ ] 对聊天信息的视频增加一个videoid就像imageid一样 - [ ] 对聊天信息的视频增加一个videoid就像imageid一样
- [ ] 修复generate_responce_for_image方法有的时候会对同一张图片生成两次描述的问题 - [ ] 修复generate_responce_for_image方法有的时候会对同一张图片生成两次描述的问题
- [ ] 主动思考的通用提示词改进 - [ ] 主动思考的通用提示词改进
- [ ] 添加贴表情聊天流判断,过滤好友 - [x] 添加贴表情聊天流判断,过滤好友
- 大工程 - 大工程

View File

@@ -217,6 +217,33 @@ class ImageDescriptions(Base):
) )
class Videos(Base):
"""视频信息模型"""
__tablename__ = 'videos'
id = Column(Integer, primary_key=True, autoincrement=True)
video_id = Column(Text, nullable=False, default="")
video_hash = Column(get_string_field(64), nullable=False, index=True, unique=True)
description = Column(Text, nullable=True)
path = Column(get_string_field(500), nullable=False, unique=True)
count = Column(Integer, nullable=False, default=1)
timestamp = Column(Float, nullable=False)
vlm_processed = Column(Boolean, nullable=False, default=False)
# 视频特有属性
duration = Column(Float, nullable=True) # 视频时长(秒)
frame_count = Column(Integer, nullable=True) # 总帧数
fps = Column(Float, nullable=True) # 帧率
resolution = Column(Text, nullable=True) # 分辨率
file_size = Column(Integer, nullable=True) # 文件大小(字节)
__table_args__ = (
Index('idx_videos_video_hash', 'video_hash'),
Index('idx_videos_path', 'path'),
Index('idx_videos_timestamp', 'timestamp'),
)
class OnlineTime(Base): class OnlineTime(Base):
"""在线时长记录模型""" """在线时长记录模型"""
__tablename__ = 'online_time' __tablename__ = 'online_time'

View File

@@ -10,6 +10,8 @@ import cv2
import tempfile import tempfile
import asyncio import asyncio
import base64 import base64
import hashlib
import time
from PIL import Image from PIL import Image
from pathlib import Path from pathlib import Path
from typing import List, Tuple, Optional, Dict from typing import List, Tuple, Optional, Dict
@@ -18,6 +20,7 @@ import io
from src.llm_models.utils_model import LLMRequest from src.llm_models.utils_model import LLMRequest
from src.config.config import global_config, model_config from src.config.config import global_config, model_config
from src.common.logger import get_logger from src.common.logger import get_logger
from src.common.database.sqlalchemy_models import get_db_session, Videos
logger = get_logger("src.multimodal.video_analyzer") logger = get_logger("src.multimodal.video_analyzer")
@@ -98,6 +101,44 @@ class VideoAnalyzer:
logger.info(f"✅ 视频分析器初始化完成,分析模式: {self.analysis_mode}") logger.info(f"✅ 视频分析器初始化完成,分析模式: {self.analysis_mode}")
def _calculate_video_hash(self, video_data: bytes) -> str:
"""计算视频文件的hash值"""
hash_obj = hashlib.sha256()
hash_obj.update(video_data)
return hash_obj.hexdigest()
def _check_video_exists(self, video_hash: str) -> Optional[Videos]:
"""检查视频是否已经分析过"""
try:
with get_db_session() as session:
return session.query(Videos).filter(Videos.video_hash == video_hash).first()
except Exception as e:
self.logger.warning(f"检查视频是否存在时出错: {e}")
return None
def _store_video_result(self, video_hash: str, description: str, path: str = "", metadata: Optional[Dict] = None) -> Optional[Videos]:
"""存储视频分析结果到数据库"""
try:
with get_db_session() as session:
# 如果path为空使用hash作为路径
if not path:
path = f"video_{video_hash[:16]}.unknown"
video_record = Videos(
video_hash=video_hash,
description=description,
path=path,
timestamp=time.time()
)
session.add(video_record)
session.commit()
session.refresh(video_record)
self.logger.info(f"✅ 视频分析结果已保存到数据库hash: {video_hash[:16]}...")
return video_record
except Exception as e:
self.logger.error(f"存储视频分析结果时出错: {e}")
return None
def set_analysis_mode(self, mode: str): def set_analysis_mode(self, mode: str):
"""设置分析模式""" """设置分析模式"""
if mode in ["batch", "sequential", "auto"]: if mode in ["batch", "sequential", "auto"]:
@@ -309,6 +350,16 @@ class VideoAnalyzer:
if not video_bytes: if not video_bytes:
return {"summary": "❌ 视频数据为空"} return {"summary": "❌ 视频数据为空"}
# 计算视频hash值
video_hash = self._calculate_video_hash(video_bytes)
logger.info(f"视频hash: {video_hash[:16]}...")
# 检查数据库中是否已存在该视频的分析结果
existing_video = self._check_video_exists(video_hash)
if existing_video:
logger.info(f"✅ 找到已存在的视频分析结果,直接返回 (id: {existing_video.id})")
return {"summary": existing_video.description}
# 创建临时文件保存视频数据 # 创建临时文件保存视频数据
with tempfile.NamedTemporaryFile(delete=False, suffix='.mp4') as temp_file: with tempfile.NamedTemporaryFile(delete=False, suffix='.mp4') as temp_file:
temp_file.write(video_bytes) temp_file.write(video_bytes)
@@ -321,6 +372,20 @@ class VideoAnalyzer:
# 使用临时文件进行分析 # 使用临时文件进行分析
result = await self.analyze_video(temp_path, question) result = await self.analyze_video(temp_path, question)
# 保存分析结果到数据库
metadata = {
"filename": filename,
"file_size": len(video_bytes),
"analysis_timestamp": time.time()
}
self._store_video_result(
video_hash=video_hash,
description=result,
path=filename or "",
metadata=metadata
)
return {"summary": result} return {"summary": result}
finally: finally:
# 清理临时文件 # 清理临时文件

View File

@@ -3,6 +3,7 @@ from collections import deque
# 导入新插件系统 # 导入新插件系统
from src.plugin_system import BaseAction, ActionActivationType, ChatMode from src.plugin_system import BaseAction, ActionActivationType, ChatMode
from src.plugin_system.base.component_types import ChatType
# 导入依赖的系统组件 # 导入依赖的系统组件
from src.common.logger import get_logger from src.common.logger import get_logger
@@ -17,6 +18,7 @@ class NoReplyAction(BaseAction):
focus_activation_type = ActionActivationType.NEVER focus_activation_type = ActionActivationType.NEVER
normal_activation_type = ActionActivationType.NEVER normal_activation_type = ActionActivationType.NEVER
mode_enable = ChatMode.FOCUS mode_enable = ChatMode.FOCUS
chat_type_allow = ChatType.GROUP
parallel_action = False parallel_action = False
# 动作基本信息 # 动作基本信息