video_service.py 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. import cv2
  2. import os
  3. import uuid
  4. from app.core.config import settings
  5. from app.core.logger import get_logger
  6. from app.schemas.models import CardInfoInput, CardInfoOutput
  7. logger = get_logger("VideoService")
  8. class VideoService:
  9. def time_str_to_ms(self, time_str: str) -> int:
  10. try:
  11. parts = list(map(int, time_str.split(':')))
  12. if len(parts) == 3:
  13. h, m, s = parts
  14. return (h * 3600 + m * 60 + s) * 1000
  15. elif len(parts) == 2:
  16. m, s = parts
  17. return (m * 60 + s) * 1000
  18. return 0
  19. except ValueError:
  20. return 0
  21. def capture_frames(self, video_path: str, cards: list[CardInfoInput]) -> list[CardInfoOutput]:
  22. if not os.path.exists(video_path):
  23. logger.error(f"❌ 找不到视频文件: {video_path}")
  24. raise FileNotFoundError(f"Video file not found: {video_path}")
  25. logger.info(f"🎬 打开视频文件: {video_path}")
  26. logger.info(f"📋 待处理卡片数量: {len(cards)}")
  27. cap = cv2.VideoCapture(video_path)
  28. output_list = []
  29. success_count = 0
  30. for idx, card_input in enumerate(cards):
  31. # 将 Input 模型转为 Output 模型 (此时 path 为 None)
  32. card_output = CardInfoOutput(**card_input.dict())
  33. time_ms = self.time_str_to_ms(card_output.time)
  34. logger.info(
  35. f"📸 [{idx + 1}/{len(cards)}] 正在截取 {card_output.time} ({time_ms}ms) - {card_output.card_name_cn or '未知卡名'}")
  36. # 设定位置
  37. cap.set(cv2.CAP_PROP_POS_MSEC, time_ms)
  38. ret, frame = cap.read()
  39. if ret:
  40. filename = f"{uuid.uuid4()}_{time_ms}.jpg"
  41. save_path = os.path.join(settings.FRAMES_DIR, filename)
  42. try:
  43. cv2.imwrite(save_path, frame)
  44. card_output.frame_image_path = save_path
  45. success_count += 1
  46. logger.info(f" ✅ 保存成功: {filename}")
  47. except Exception as e:
  48. logger.error(f" ❌ 保存图片失败: {e}")
  49. else:
  50. logger.warning(f" ⚠️ 无法读取视频帧 (可能时间戳超出视频长度)")
  51. output_list.append(card_output)
  52. cap.release()
  53. logger.info(f"🏁 截取任务结束. 成功: {success_count}, 总数: {len(cards)}")
  54. return output_list