# Sora2API 插件修复说明 ## 问题诊断 通过分析日志文件 `hookbot.2025-11-25_22-18-49_952160.log`,发现问题**不是因为提示词太长**,而是: ### 真正的问题:API 返回的视频 URL 存在两种情况 1. **成功的情况**(域名:`sora.goodnet.icu`) - URL 示例:`https://sora.goodnet.icu/tmp/88dd7f3c0fa08c2115b05dae056b5c4e.mp4` - 可以正常下载 ✅ 2. **失败的情况**(域名:`oscdn2.dyysy.com`) - URL 示例:`https://oscdn2.dyysy.com/MP4/s_6925bc764f388191b51a9f46aa27b0f9.mp4` - 立即返回 404 错误 ❌ ### 日志证据 ``` # 成功案例 22:18:51 | Sora2API请求: sora-video-portrait-15s, 提示词: 科比打篮球... 22:22:47 | 提取到视频URL: https://sora.goodnet.icu/tmp/88dd7f3c0fa08c2115b05dae056b5c4e.mp4 22:22:58 | 视频下载成功 ✅ # 失败案例 22:25:45 | Sora2API请求: sora-video-landscape-15s, 提示词: 科比在篮球场上打篮球... 22:26:00 | 提取到视频URL: https://oscdn2.dyysy.com/MP4/s_6925bc764f388191b51a9f46aa27b0f9.mp4 22:26:03 | ERROR | 下载视频失败: Client error '404 Not Found' ❌ ``` ## 修复方案 ### 1. 改进错误日志 (main.py:180-185) **修改前:** ```python except Exception as e: logger.error(f"请求异常: {e}") continue ``` **修改后:** ```python except Exception as e: import traceback logger.error(f"请求异常: {type(e).__name__}: {str(e)}") logger.error(f"异常详情:\n{traceback.format_exc()}") logger.error(f"提示词长度: {len(prompt)} 字符") continue ``` **改进点:** - 显示完整的异常类型和堆栈跟踪 - 记录提示词长度,便于诊断 - 不再截断错误信息 ### 2. 增强调试信息 (main.py:106-109) **修改前:** ```python logger.info(f"Sora2API请求: {model}, 提示词: {prompt[:50]}...") ``` **修改后:** ```python logger.info(f"Sora2API请求: {model}, 提示词长度: {len(prompt)} 字符") logger.debug(f"完整提示词: {prompt}") logger.debug(f"请求URL: {url}") logger.debug(f"Payload大小: {len(str(payload))} 字节") ``` **改进点:** - 记录提示词长度而不是截断内容 - 添加 DEBUG 级别的详细信息 - 便于排查请求问题 ### 3. 添加长提示词警告 (main.py:120-122) ```python # 添加提示词长度检查和警告 if len(prompt) > 1000: logger.warning(f"提示词较长 ({len(prompt)} 字符),可能影响处理速度") ``` **改进点:** - 当提示词超过 1000 字符时发出警告 - 帮助识别潜在的性能问题 ### 4. 改进视频下载函数 (main.py:218-266) **核心改进:404 错误智能处理** ```python async def _download_video(self, url: str, retry_on_404: bool = True) -> Optional[str]: """ 下载视频到本地 Args: url: 视频URL retry_on_404: 是否在404时返回None以触发重试 """ try: # ... 下载逻辑 ... # 检查是否是404错误 if response.status_code == 404: logger.warning(f"视频URL返回404: {url}") if retry_on_404: logger.info("将触发重试以获取新的视频链接") return None # 返回 None 触发重试 response.raise_for_status() # ... 保存文件 ... except httpx.HTTPStatusError as e: if e.response.status_code == 404: logger.warning(f"视频URL返回404: {url}") if retry_on_404: logger.info("将触发重试以获取新的视频链接") return None # 返回 None 触发重试 logger.error(f"下载视频失败: {e}") ``` **改进点:** - 专门处理 404 错误 - 返回 `None` 触发上层重试逻辑 - 区分 404 和其他错误类型 ### 5. 添加自动重试机制 (main.py:170-179) ```python if video_url: # 下载视频 video_path = await self._download_video(video_url) if video_path: logger.success("成功生成视频") return [video_path] else: # 下载失败(可能是404),继续重试 logger.warning(f"视频下载失败,将重试 ({attempt + 1}/{max_retry})") continue ``` **改进点:** - 当下载失败时,自动重新请求 API - 利用现有的重试机制(max_retry_attempts) - 每次重试都会获取新的视频 URL ## 工作原理 ### 修复前的流程: ``` 1. 请求 API 生成视频 2. 获取视频 URL (可能是 oscdn2.dyysy.com) 3. 尝试下载 → 404 错误 4. ❌ 失败,不再重试 ``` ### 修复后的流程: ``` 1. 请求 API 生成视频 2. 获取视频 URL (可能是 oscdn2.dyysy.com) 3. 尝试下载 → 404 错误 4. 检测到 404,返回 None 5. 触发重试机制 6. 重新请求 API 生成视频 7. 获取新的视频 URL (可能是 sora.goodnet.icu) 8. 下载成功 ✅ ``` ## 配置说明 在 `config.toml` 中可以调整重试次数: ```toml [generation] max_retry_attempts = 3 # 最大重试次数,建议保持 3 次 ``` ## 测试建议 1. **运行测试脚本**(可选): ```bash cd WechatHookBot/plugins/Sora2API python test_sora.py ``` 2. **实际测试**: - 发送命令:`/sora 科比打篮球被蔡徐坤单防` - 观察日志输出 - 如果遇到 404,应该会自动重试 3. **查看日志**: ```bash # 查看最新日志 tail -f WechatHookBot/logs/hookbot.log # 搜索 Sora 相关日志 grep "Sora2API" WechatHookBot/logs/hookbot.log ``` ## 预期效果 修复后,即使 API 返回 `oscdn2.dyysy.com` 域名的 404 链接,插件也会: 1. ✅ 检测到 404 错误 2. ✅ 记录警告日志 3. ✅ 自动重试请求 4. ✅ 获取新的视频链接 5. ✅ 成功下载并发送视频 ## 补充修复:ReadTimeout 超时问题 ### 问题描述 在处理较长提示词(如 370 字符)时,可能会遇到 `httpx.ReadTimeout` 错误: ``` ERROR | 请求异常: ReadTimeout: ERROR | 提示词长度: 370 字符 ``` ### 原因分析 - 视频生成需要较长时间(尤其是长提示词) - 流式响应在等待数据时可能超过读取超时限制 - 原超时配置(300秒)对于复杂视频生成可能不够 ### 修复方案 #### 1. 增加超时时间 (main.py:114-121) ```python # 增加到 10 分钟 max_timeout = min(api_config["timeout"], 600) timeout = httpx.Timeout( connect=10.0, # 连接超时:10秒 read=max_timeout, # 读取超时:10分钟(两次数据块之间的最大间隔) write=10.0, # 写入超时:10秒 pool=10.0 # 连接池超时:10秒 ) ``` **关键点:** - 对于流式响应,`read` 超时是指**两次数据块之间的最大间隔**,而不是总时间 - 增加到 600 秒(10分钟)以应对长时间的视频生成 #### 2. 专门处理 ReadTimeout (main.py:200-203) ```python except httpx.ReadTimeout: logger.warning(f"读取超时(ReadTimeout),可能是视频生成时间过长,重试中... ({attempt + 1}/{max_retry})") logger.info(f"提示词长度: {len(prompt)} 字符,建议缩短提示词或增加超时时间") continue ``` **改进点:** - 单独捕获 `ReadTimeout` 异常 - 提供更友好的错误提示 - 自动重试(最多 3 次) #### 3. 更新配置文件 (config.toml) ```toml [api] timeout = 600 # 请求超时时间(秒),流式响应的读取超时 ``` ### 工作流程 ``` 1. 发送长提示词请求 2. 开始接收流式响应 3. 等待视频生成... 4. 如果超过 10 分钟无数据 → ReadTimeout 5. 捕获异常,记录日志 6. 自动重试(attempt 2/3) 7. 重新请求 API 8. 成功接收响应 ✅ ``` ## 注意事项 - 如果 3 次重试都失败,会返回错误提示 - 每次重试之间有指数退避延迟(2^attempt 秒,最多 10 秒) - 404 错误不会计入熔断器的失败次数 - ReadTimeout 会自动重试,不影响其他请求 - 代理配置目前被临时禁用(第 124、241 行),如需启用请取消注释 - 如果经常遇到超时,建议: - 缩短提示词长度 - 或在 `config.toml` 中增加 `timeout` 值(当前 600 秒) ## 相关文件 - `main.py` - 主要修改文件 - `config.toml` - 配置文件 - `test_sora.py` - 测试脚本(新增) - `修复说明.md` - 本文档