feat:修复一些BUG
This commit is contained in:
@@ -28,6 +28,7 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import base64
|
||||
import io
|
||||
import json
|
||||
import uuid
|
||||
from dataclasses import dataclass, field
|
||||
@@ -37,6 +38,14 @@ from typing import Any, Dict, Optional, TYPE_CHECKING
|
||||
import aiohttp
|
||||
from loguru import logger
|
||||
|
||||
# 图片处理支持
|
||||
try:
|
||||
from PIL import Image
|
||||
PIL_AVAILABLE = True
|
||||
except ImportError:
|
||||
PIL_AVAILABLE = False
|
||||
logger.warning("[ImageProcessor] Pillow 未安装,GIF 转换功能不可用")
|
||||
|
||||
# 可选代理支持
|
||||
try:
|
||||
from aiohttp_socks import ProxyConnector
|
||||
@@ -433,6 +442,60 @@ class ImageProcessor:
|
||||
logger.error(traceback.format_exc())
|
||||
return ""
|
||||
|
||||
def _convert_gif_to_png(self, image_base64: str) -> str:
|
||||
"""
|
||||
将 GIF 图片转换为 PNG(提取第一帧)
|
||||
|
||||
Args:
|
||||
image_base64: GIF 图片的 base64 数据(带 data URI 前缀)
|
||||
|
||||
Returns:
|
||||
PNG 图片的 base64 数据(带 data URI 前缀),失败返回原数据
|
||||
"""
|
||||
if not PIL_AVAILABLE:
|
||||
logger.warning("[ImageProcessor] Pillow 未安装,无法转换 GIF")
|
||||
return image_base64
|
||||
|
||||
try:
|
||||
# 提取 base64 数据部分
|
||||
if "," in image_base64:
|
||||
base64_data = image_base64.split(",", 1)[1]
|
||||
else:
|
||||
base64_data = image_base64
|
||||
|
||||
# 解码 base64
|
||||
gif_bytes = base64.b64decode(base64_data)
|
||||
|
||||
# 使用 Pillow 打开 GIF 并提取第一帧
|
||||
img = Image.open(io.BytesIO(gif_bytes))
|
||||
|
||||
# 转换为 RGB 模式(去除透明通道)
|
||||
if img.mode in ('RGBA', 'LA', 'P'):
|
||||
# 创建白色背景
|
||||
background = Image.new('RGB', img.size, (255, 255, 255))
|
||||
if img.mode == 'P':
|
||||
img = img.convert('RGBA')
|
||||
background.paste(img, mask=img.split()[-1] if img.mode == 'RGBA' else None)
|
||||
img = background
|
||||
elif img.mode != 'RGB':
|
||||
img = img.convert('RGB')
|
||||
|
||||
# 保存为 PNG
|
||||
output = io.BytesIO()
|
||||
img.save(output, format='PNG', optimize=True)
|
||||
png_bytes = output.getvalue()
|
||||
|
||||
# 编码为 base64
|
||||
png_base64 = base64.b64encode(png_bytes).decode()
|
||||
result = f"data:image/png;base64,{png_base64}"
|
||||
|
||||
logger.debug(f"[ImageProcessor] GIF 已转换为 PNG,原大小: {len(gif_bytes)} 字节,新大小: {len(png_bytes)} 字节")
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[ImageProcessor] GIF 转换失败: {e}")
|
||||
return image_base64
|
||||
|
||||
async def generate_description(
|
||||
self,
|
||||
image_base64: str,
|
||||
@@ -450,6 +513,11 @@ class ImageProcessor:
|
||||
Returns:
|
||||
图片描述文本,失败返回空字符串
|
||||
"""
|
||||
# 检测并转换 GIF 格式(大多数视觉 API 不支持 GIF)
|
||||
if image_base64.startswith("data:image/gif"):
|
||||
logger.debug("[ImageProcessor] 检测到 GIF 格式,转换为 PNG...")
|
||||
image_base64 = self._convert_gif_to_png(image_base64)
|
||||
|
||||
description_model = model or self.config.model
|
||||
|
||||
messages = [
|
||||
@@ -612,6 +680,13 @@ class ImageProcessor:
|
||||
logger.error(f"[ImageProcessor] 视频 API 错误: {resp.status}, {error_text[:300]}")
|
||||
return ""
|
||||
|
||||
# 检查响应类型是否为 JSON
|
||||
content_type = resp.headers.get('Content-Type', '')
|
||||
if 'application/json' not in content_type:
|
||||
error_text = await resp.text()
|
||||
logger.error(f"[ImageProcessor] 视频 API 返回非 JSON 响应: Content-Type={content_type}, Body={error_text[:500]}")
|
||||
return ""
|
||||
|
||||
result = await resp.json()
|
||||
|
||||
# 检查安全过滤
|
||||
|
||||
Reference in New Issue
Block a user